Tempo ramps - fix messed up meter section state, audio-locked tempos ignore the bbt...
[ardour.git] / libs / ardour / tempo.cc
index 5d027462100a36a73467aa604a3efb68831f7304..9f81f616e4919383da7ac70662ea89b36c35906a 100644 (file)
 
 #include <algorithm>
 #include <stdexcept>
+#include <cmath>
 
 #include <unistd.h>
 
-#include <cmath>
+#include <glibmm/threads.h>
 
-#include <glibmm/thread.h>
+#include "pbd/enumwriter.h"
 #include "pbd/xml++.h"
-#include "evoral/types.hpp"
+#include "evoral/Beats.hpp"
+
 #include "ardour/debug.h"
+#include "ardour/lmath.h"
 #include "ardour/tempo.h"
-#include "ardour/utils.h"
 
 #include "i18n.h"
 #include <locale.h>
@@ -45,51 +47,70 @@ using Timecode::BBT_Time;
 Meter    TempoMap::_default_meter (4.0, 4.0);
 Tempo    TempoMap::_default_tempo (120.0);
 
-double 
-Tempo::frames_per_beat (framecnt_t sr) const
-{
-       return  (60.0 * sr) / _beats_per_minute;
-}
-
 /***********************************************************************/
 
-double 
-Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
+double
+Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
 {
+       /* This is tempo- and meter-sensitive. The number it returns
+          is based on the interval between any two lines in the
+          grid that is constructed from tempo and meter sections.
+
+          The return value IS NOT interpretable in terms of "beats".
+       */
+
        return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
 }
 
 double
 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
 {
-       return frames_per_division (tempo, sr) * _divisions_per_bar;
+       return frames_per_grid (tempo, sr) * _divisions_per_bar;
 }
 
+
 /***********************************************************************/
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
 TempoSection::TempoSection (const XMLNode& node)
-       : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
+       : MetricSection (0.0), Tempo (TempoMap::default_tempo())
 {
        const XMLProperty *prop;
-       BBT_Time start;
-       LocaleGuard lg (X_("POSIX"));
-
-       if ((prop = node.property ("start")) == 0) {
-               error << _("TempoSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
+       LocaleGuard lg;
+       BBT_Time bbt;
+       double beat;
+       uint32_t frame;
+
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                           &bbt.bars,
+                           &bbt.beats,
+                           &bbt.ticks) == 3) {
+                       /* legacy session - start used to be in bbt*/
+                       _legacy_bbt = bbt;
+                       beat = -1.0;
+                       set_beat (beat);
+               }
+       } else {
+               warning << _("TempoSection XML node has no \"start\" property") << endmsg;
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &start.bars,
-                   &start.beats,
-                   &start.ticks) < 3) {
-               error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
-               throw failed_constructor();
-       }
 
-       set_start (start);
+       if ((prop = node.property ("beat")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+                       error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
+               } else {
+                       set_beat (beat);
+               }
+       }
+       if ((prop = node.property ("frame")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
+                       error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
+               } else {
+                       set_frame (frame);
+               }
+       }
 
        if ((prop = node.property ("beats-per-minute")) == 0) {
                error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
@@ -126,6 +147,18 @@ TempoSection::TempoSection (const XMLNode& node)
                        throw failed_constructor();
                }
        }
+
+       if ((prop = node.property ("tempo-type")) == 0) {
+               _type = Constant;
+       } else {
+               _type = Type (string_2_enum (prop->value(), _type));
+       }
+
+       if ((prop = node.property ("lock-style")) == 0) {
+               set_position_lock_style (MusicTime);
+       } else {
+               set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
+       }
 }
 
 XMLNode&
@@ -133,13 +166,12 @@ TempoSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg;
 
-       snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 start().bars,
-                 start().beats,
-                 start().ticks);
-       root->add_property ("start", buf);
+       snprintf (buf, sizeof (buf), "%f", beat());
+       root->add_property ("beat", buf);
+       snprintf (buf, sizeof (buf), "%li", frame());
+       root->add_property ("frame", buf);
        snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
@@ -148,6 +180,8 @@ TempoSection::get_state() const
        // root->add_property ("bar-offset", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
+       root->add_property ("tempo-type", enum_2_string (_type));
+       root->add_property ("lock-style", enum_2_string (position_lock_style()));
 
        return *root;
 }
@@ -156,62 +190,330 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) / 
-               (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
+       _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
+               (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
+
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
+}
+
+void
+TempoSection::set_type (Type type)
+{
+       _type = type;
+}
+
+/** returns the tempo at the zero-based (relative to tempo section) frame.
+*/
+double
+TempoSection::tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const
+{
+
+       if (_type == Constant) {
+               return beats_per_minute();
+       }
+
+       return tick_tempo_at_time (frame_to_minute (frame, frame_rate)) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based frame (relative to tempo section)
+   where the tempo occurs.
+*/
+framepos_t
+TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return 0;
+       }
+
+       return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate);
+}
+
+/** returns the zero-based tick (relative to tempo section)
+   where the zero-based frame (relative to tempo section)
+   lies.
+*/
+double
+TempoSection::tick_at_frame (framepos_t frame, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
+       }
+
+       return tick_at_time (frame_to_minute (frame, frame_rate));
+}
+
+/** returns the zero-based frame (relative to tempo section)
+   where the zero-based tick (relative to tempo section)
+   falls.
+*/
+framepos_t
+TempoSection::frame_at_tick (double tick, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return (framepos_t) floor ((tick  / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
+       }
+
+       return minute_to_frame (time_at_tick (tick), frame_rate);
+}
+
+/** returns the zero-based beat (relative to tempo section)
+   where the zero-based frame (relative to tempo section)
+   lies.
+*/
+double
+TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
+{
+       return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based frame (relative to tempo section start frame)
+   where the zero-based beat (relative to tempo section start)
+   falls.
+*/
+
+framepos_t
+TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
+{
+       return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
+}
+
+/*
+Ramp Overview
+
+      |                     *
+Tempo |                   *
+Tt----|-----------------*|
+Ta----|--------------|*  |
+      |            * |   |
+      |         *    |   |
+      |     *        |   |
+T0----|*             |   |
+  *   |              |   |
+      _______________|___|____
+      time           a   t (next tempo)
+      [        c         ] defines c
+
+Duration in beats at time a is the integral of some Tempo function.
+In our case, the Tempo function (Tempo at time t) is
+T(t) = T0(e^(ct))
+
+with function constant
+c = log(Ta/T0)/a
+so
+a = log(Ta/T0)/c
+
+The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
+b(t) = T0(e^(ct) - 1) / c
+
+To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
+t(b) = log((cb / T0) + 1) / c
+
+The time t at which Tempo T occurs is a as above:
+t(T) = log(T / T0) / c
+
+We define c for this tempo ramp by placing a new tempo section at some time t after this one.
+Our problem is that we usually don't know t.
+We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
+Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
+t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
+
+By substituting our expanded t as a in the c function above, our problem is reduced to:
+c = T0 (e^(log (Ta / T0)) - 1) / b
+
+We can now store c for future time calculations.
+If the following tempo section (the one that defines c in conjunction with this one)
+is changed or moved, c is no longer valid.
+
+Most of this stuff is taken from this paper:
+
+WHERE’S THE BEAT?
+TOOLS FOR DYNAMIC TEMPO CALCULATIONS
+Jan C. Schacher
+Martin Neukom
+Zurich University of Arts
+Institute for Computer Music and Sound Technology
+
+https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
+
+*/
+
+/* set this ramp's function constant using the end tempo and duration in beats of some later tempo section*/
+void
+TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
+{
+       double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
+       _c_func = ticks_per_minute() *  (exp (log_tempo_ratio) - 1) / (end_beat * BBT_Time::ticks_per_beat);
+}
+
+/* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from this tempo section */
+double
+TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
+}
+
+framecnt_t
+TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+{
+       return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
+}
+
+double
+TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
+{
+       return (frame / (double) frame_rate) / 60.0;
+}
+
+/* position function */
+double
+TempoSection::a_func (double end_tpm, double c_func) const
+{
+       return log (end_tpm / ticks_per_minute()) /  c_func;
+}
+
+/*function constant*/
+double
+TempoSection::c_func (double end_tpm, double end_time) const
+{
+       return log (end_tpm / ticks_per_minute()) /  end_time;
+}
+
+/* tempo in tpm at time in minutes */
+double
+TempoSection::tick_tempo_at_time (double time) const
+{
+       return exp (_c_func * time) * ticks_per_minute();
+}
+
+/* time in minutes at tempo in tpm */
+double
+TempoSection::time_at_tick_tempo (double tick_tempo) const
+{
+       return log (tick_tempo / ticks_per_minute()) / _c_func;
+}
+
+/* tick at time in minutes */
+double
+TempoSection::tick_at_time (double time) const
+{
+       return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
+}
+
+/* time in minutes at tick */
+double
+TempoSection::time_at_tick (double tick) const
+{
+       return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
+}
+
+/* beat at time in minutes */
+double
+TempoSection::beat_at_time (double time) const
+{
+       return tick_at_time (time) / BBT_Time::ticks_per_beat;
+}
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
+/* time in munutes at beat */
+double
+TempoSection::time_at_beat (double beat) const
+{
+       return time_at_tick (beat * BBT_Time::ticks_per_beat);
 }
 
 void
 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 {
-       BBT_Time new_start;
+       double new_beat;
 
        if (_bar_offset < 0.0) {
                /* not set yet */
                return;
        }
 
-       new_start.bars = start().bars;
-       
-       double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset;
-       new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
-       new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
+       new_beat = beat();
 
-       /* remember the 1-based counting properties of beats */
-       new_start.beats += 1;
-                                           
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", 
-                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
+       double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
+       new_beat = ticks / BBT_Time::ticks_per_beat;
 
-       set_start (new_start);
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
+                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
+
+       set_beat (new_beat);
 }
 
 /***********************************************************************/
 
+/*
+if a meter section is position locked to frames, then it can only be at 1|1|0.
+thus we can have multiple 1|1|0s in the session tempo map.
+
+like this:
+
+BBT  1|1|0 2|1|0 3|1|0 1|1|0
+beat   0     4     8    12
+
+all other meter sections are locked to beats.
+
+the beat of a meter section is used to find its position rather than the stored bbt.
+*/
+
 const string MeterSection::xml_state_node_name = "Meter";
 
 MeterSection::MeterSection (const XMLNode& node)
-       : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
+       : MetricSection (0.0), Meter (TempoMap::default_meter())
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
        BBT_Time start;
-       LocaleGuard lg (X_("POSIX"));
-
-       if ((prop = node.property ("start")) == 0) {
+       LocaleGuard lg;
+       const XMLProperty *prop;
+       BBT_Time bbt;
+       double beat = 0.0;
+       framepos_t frame = 0;
+       pair<double, BBT_Time> start;
+
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+               } else {
+                       /* legacy session - start used to be in bbt*/
+                       beat = -1.0;
+               }
+       } else {
                error << _("MeterSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &start.bars,
-                   &start.beats,
-                   &start.ticks) < 3) {
-               error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
-               throw failed_constructor();
+       if ((prop = node.property ("beat")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+                       error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
+               }
+       }
+
+       start.first = beat;
+
+       if ((prop = node.property ("bbt")) == 0) {
+               error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
+       } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+               error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
+               //throw failed_constructor();
        }
 
-       set_start (start);
+       start.second = bbt;
+
+       set_beat (start);
+
+       if ((prop = node.property ("frame")) != 0) {
+               if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
+                       error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
+               } else {
+                       set_frame (frame);
+               }
+       }
 
        /* beats-per-bar is old; divisions-per-bar is new */
 
@@ -219,11 +521,10 @@ MeterSection::MeterSection (const XMLNode& node)
                if ((prop = node.property ("beats-per-bar")) == 0) {
                        error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
                        throw failed_constructor();
-               } 
+               }
        }
-
        if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
-               error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
+               error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
                throw failed_constructor();
        }
 
@@ -231,12 +532,18 @@ MeterSection::MeterSection (const XMLNode& node)
                error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
                throw failed_constructor();
        }
-
        if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
                error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
                throw failed_constructor();
        }
 
+       if ((prop = node.property ("lock-style")) == 0) {
+               warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
+               set_position_lock_style (PositionLockStyle::MusicTime);
+       } else {
+               set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
+       }
+
        if ((prop = node.property ("movable")) == 0) {
                error << _("MeterSection XML node has no \"movable\" property") << endmsg;
                throw failed_constructor();
@@ -250,15 +557,20 @@ MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg;
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 start().bars,
-                 start().beats,
-                 start().ticks);
-       root->add_property ("start", buf);
+                 bbt().bars,
+                 bbt().beats,
+                 bbt().ticks);
+       root->add_property ("bbt", buf);
+       snprintf (buf, sizeof (buf), "%lf", beat());
+       root->add_property ("beat", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
+       snprintf (buf, sizeof (buf), "%li", frame());
+       root->add_property ("frame", buf);
+       root->add_property ("lock-style", enum_2_string (position_lock_style()));
        snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
        root->add_property ("divisions-per-bar", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
@@ -271,38 +583,40 @@ MeterSection::get_state() const
 
 struct MetricSectionSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->start() < b->start();
+           return a->beat() < b->beat();
+    }
+};
+
+struct MetricSectionFrameSorter {
+    bool operator() (const MetricSection* a, const MetricSection* b) {
+           return a->frame() < b->frame();
     }
 };
 
 TempoMap::TempoMap (framecnt_t fr)
 {
-       metrics = new Metrics;
-       _map = new BBTPointList;
        _frame_rate = fr;
-       last_bbt_valid = false;
        BBT_Time start;
 
        start.bars = 1;
        start.beats = 1;
        start.ticks = 0;
 
-       TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
-       MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
+       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
+       MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        t->set_movable (false);
        m->set_movable (false);
 
        /* note: frame time is correct (zero) for both of these */
 
-       metrics->push_back (t);
-       metrics->push_back (m);
+       metrics.push_back (t);
+       metrics.push_back (m);
+
 }
 
 TempoMap::~TempoMap ()
 {
-       delete metrics;
-       delete _map;
 }
 
 void
@@ -311,272 +625,459 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
        bool removed = false;
 
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
-               Metrics::iterator i;
-
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
-                       if (dynamic_cast<TempoSection*> (*i) != 0) {
-                               if (tempo.frame() == (*i)->frame()) {
-                                       if ((*i)->movable()) {
-                                               metrics->erase (i);
-                                               removed = true;
-                                               break;
-                                       }
-                               }
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               if ((removed = remove_tempo_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
        }
 
        if (removed && complete_operation) {
-               recompute_map (false);
                PropertyChanged (PropertyChange ());
        }
 }
 
+bool
+TempoMap::remove_tempo_locked (const TempoSection& tempo)
+{
+       Metrics::iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               if (dynamic_cast<TempoSection*> (*i) != 0) {
+                       if (tempo.frame() == (*i)->frame()) {
+                               if ((*i)->movable()) {
+                                       metrics.erase (i);
+                                       return true;
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
 void
 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 {
        bool removed = false;
 
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
-               Metrics::iterator i;
-
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
-                       if (dynamic_cast<MeterSection*> (*i) != 0) {
-                               if (tempo.frame() == (*i)->frame()) {
-                                       if ((*i)->movable()) {
-                                               metrics->erase (i);
-                                               removed = true;
-                                               break;
-                                       }
-                               }
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               if ((removed = remove_meter_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
        }
 
        if (removed && complete_operation) {
-               recompute_map (true);
                PropertyChanged (PropertyChange ());
        }
 }
 
+bool
+TempoMap::remove_meter_locked (const MeterSection& tempo)
+{
+       Metrics::iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               if (dynamic_cast<MeterSection*> (*i) != 0) {
+                       if (tempo.frame() == (*i)->frame()) {
+                               if ((*i)->movable()) {
+                                       metrics.erase (i);
+                                       return true;
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
 void
 TempoMap::do_insert (MetricSection* section)
 {
        bool need_add = true;
 
-       assert (section->start().ticks == 0);
-
        /* we only allow new meters to be inserted on beat 1 of an existing
-        * measure. 
+        * measure.
         */
+       MeterSection* m = 0;
+       if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
+               assert (m->bbt().ticks == 0);
 
-       if (dynamic_cast<MeterSection*>(section)) {
+               if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
-               /* we need to (potentially) update the BBT times of tempo
-                  sections based on this new meter.
-               */
-               
-               if ((section->start().beats != 1) || (section->start().ticks != 0)) {
-                       
-                       BBT_Time corrected = section->start();
-                       corrected.beats = 1;
-                       corrected.ticks = 0;
-                       
+                       pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
+                       corrected.second.beats = 1;
+                       corrected.second.ticks = 0;
+                       corrected.first = bbt_to_beats_locked (corrected.second);
                        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);
+                                                  m->bbt(), corrected.second) << endmsg;
+                       m->set_beat (corrected);
                }
        }
 
-       Metrics::iterator i;
-
        /* Look for any existing MetricSection that is of the same type and
-          at the same time as the new one, and remove it before adding
-          the new one.
+          in the same bar as the new one, and remove it before adding
+          the new one. Note that this means that if we find a matching,
+          existing section, we can break out of the loop since we're
+          guaranteed that there is only one such match.
        */
 
-       Metrics::iterator to_remove = metrics->end ();
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
-       for (i = metrics->begin(); i != metrics->end(); ++i) {
+               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+               TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
 
-               int const c = (*i)->compare (*section);
+               if (tempo && insert_tempo) {
 
-               if (c < 0) {
-                       /* this section is before the one to be added; go back round */
-                       continue;
-               } else if (c > 0) {
-                       /* this section is after the one to be added; there can't be any at the same time */
-                       break;
-               }
+                       /* Tempo sections */
+                       PositionLockStyle const ipl = insert_tempo->position_lock_style();
+                       if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame())) {
+
+                               if (!tempo->movable()) {
+
+                                       /* can't (re)move this section, so overwrite
+                                        * its data content (but not its properties as
+                                        * a section).
+                                        */
+
+                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
+                                       need_add = false;
+                               } else {
+                                       metrics.erase (i);
+                               }
+                               break;
+                       }
 
-               /* hacky comparison of type */
-               bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
-               bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
+               } else if (!tempo && !insert_tempo) {
 
-               if (iter_is_tempo == insert_is_tempo) {
+                       /* Meter Sections */
+                       MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+                       MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+                       PositionLockStyle const ipl = insert_meter->position_lock_style();
 
-                       if (!(*i)->movable()) {
+                       if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
 
-                               /* can't (re)move this section, so overwrite it
-                                */
+                               if (!meter->movable()) {
+
+                                       /* can't (re)move this section, so overwrite
+                                        * its data content (but not its properties as
+                                        * a section
+                                        */
 
-                               if (!iter_is_tempo) {
-                                       *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
+                                       *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
+                                       need_add = false;
                                } else {
-                                       *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
+                                       metrics.erase (i);
+
                                }
-                               need_add = false;
+
                                break;
                        }
-
-                       to_remove = i;
-                       break;
+               } else {
+                       /* non-matching types, so we don't care */
                }
        }
 
-       if (to_remove != metrics->end()) {
-               /* remove the MetricSection at the same time as the one we are about to add */
-               metrics->erase (to_remove);
-       }
-
-       /* Add the given MetricSection */
+       /* Add the given MetricSection, if we didn't just reset an existing
+        * one above
+        */
 
        if (need_add) {
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
-                       
-                       if ((*i)->compare (*section) < 0) {
-                               continue;
+               MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+               TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
+
+               Metrics::iterator i;
+               if (insert_meter) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+
+                               if (meter) {
+                                       PositionLockStyle const ipl = insert_meter->position_lock_style();
+                                       if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
+                                               break;
+                                       }
+                               }
                        }
-                       
-                       metrics->insert (i, section);
-                       break;
-               }
+               } else if (insert_tempo) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
 
-               if (i == metrics->end()) {
-                       metrics->insert (metrics->end(), section);
+                               if (tempo) {
+                                       PositionLockStyle const ipl = insert_tempo->position_lock_style();
+                                       if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
+                                               break;
+                                       }
+                               }
+                       }
                }
+
+               metrics.insert (i, section);
+               //dump (std::cerr);
        }
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
 {
-       const TempoSection& first (first_tempo());
-
-       if (ts != first) {
-               remove_tempo (ts, false);
-               add_tempo (tempo, where);
-       } else {
-               {
-                       Glib::RWLock::WriterLock lm (metrics_lock);
-                       /* cannot move the first tempo section */
-                       *((Tempo*)&first) = tempo;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection& first (first_tempo());
+               if (ts.beat() != first.beat()) {
+                       remove_tempo_locked (ts);
+                       add_tempo_locked (tempo, where, true, type);
+               } else {
+                       first.set_type (type);
+                       {
+                               /* cannot move the first tempo section */
+                               *static_cast<Tempo*>(&first) = tempo;
+                               recompute_map (false);
+                       }
                }
-
-               recompute_map (false);
        }
 
        PropertyChanged (PropertyChange ());
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
 {
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
-
-               /* new tempos always start on a beat */
-               where.ticks = 0;
-
-               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.
-                */
-
-               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;
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection& first (first_tempo());
+               if (ts.beat() != first.beat()) {
+                       remove_tempo_locked (ts);
+                       add_tempo_locked (tempo, frame, true, type);
+               } else {
+                       first.set_type (type);
+                       {
+                               /* cannot move the first tempo section */
+                               *static_cast<Tempo*>(&first) = tempo;
+                               recompute_map (false);
                        }
                }
-
-               ts->update_bar_offset_from_bbt (*meter);
-
-               /* and insert it */
-               
-               do_insert (ts);
        }
 
-       recompute_map (false);
+       PropertyChanged (PropertyChange ());
+}
+
+void
+TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+
+               if (ts.position_lock_style() == MusicTime) {
+                       std::cerr << "Music " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
+                       /* MusicTime */
+                       ts.set_beat (beat_where);
+                       Metrics::const_iterator i;
+
+                       TempoSection* prev_ts = 0;
+                       MetricSectionSorter cmp;
+                       metrics.sort (cmp);
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                                       if (t->beat() >= beat_where) {
+                                               break;
+                                       }
+
+                                       prev_ts = t;
+                               }
+                       }
+
+                       prev_ts->set_c_func_from_tempo_and_beat (ts.beats_per_minute(), ts.beat() - prev_ts->beat(), _frame_rate);
+                       ts.set_frame (prev_ts->frame_at_beat (ts.beat() - prev_ts->beat(), _frame_rate));
+               } else {
+                       std::cerr << "Audio " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
+
+                       /*AudioTime*/
+                       ts.set_frame (frame);
+                       MetricSectionFrameSorter fcmp;
+                       metrics.sort (fcmp);
+
+                       Metrics::const_iterator i;
+                       TempoSection* prev_ts = 0;
+                       TempoSection* next_ts = 0;
+
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                                       if (t->frame() >= frame) {
+                                               break;
+                                       }
+
+                                       prev_ts = t;
+                               }
+                       }
+
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                                       if (t->frame() > frame) {
+                                               next_ts = t;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (prev_ts) {
+                               /* set the start beat - we need to reset the function constant before beat calculations make sense*/
+                               prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate));
+
+                               double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), _frame_rate);
+                               double beats = beats_to_ts + prev_ts->beat();
+
+                               if (next_ts) {
+                                       if (next_ts->beat() < beats) {
+                                               /* with frame-based editing, it is possible to get in a
+                                                  situation where if the tempo was placed at the mouse pointer frame,
+                                                  the following music-based tempo would jump to an earlier frame,
+                                                  changing the beat beat of the moved tempo.
+                                                  in this case, we have to do some beat-based comparison TODO
+                                               */
+                                       } else if (prev_ts->beat() > beats) {
+                                               ts.set_beat (prev_ts->beat());
+                                       } else {
+                                               ts.set_beat (beats);
+                                       }
+                               } else {
+                                       ts.set_beat (beats);
+                                       ts.set_c_func (0.0);
+
+                               }
+                               MetricSectionSorter cmp;
+                               metrics.sort (cmp);
+                       }
+               }
+
+               recompute_map (false);
+       }
+
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_tempo_locked (tempo, where, true, type);
+       }
+
 
        PropertyChanged (PropertyChange ());
 }
 
+void
+TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_tempo_locked (tempo, frame, true, type);
+       }
+
+
+       PropertyChanged (PropertyChange ());
+}
+
+void
+TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
+{
+       TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
+
+       do_insert (ts);
+
+       if (recompute) {
+               recompute_map (false);
+       }
+}
+
+void
+TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
+{
+       TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
+
+       do_insert (ts);
+
+       if (recompute) {
+               recompute_map (false);
+       }
+}
+
 void
 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
 {
-       const MeterSection& first (first_meter());
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               MeterSection& first (first_meter());
+               if (ms.beat() != first.beat()) {
+                       remove_meter_locked (ms);
+                       add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
+               } else {
+                       /* cannot move the first meter section */
+                       *static_cast<Meter*>(&first) = meter;
+                       recompute_map (true);
+               }
+       }
 
-       if (ms != first) {
-               remove_meter (ms, false);
-               add_meter (meter, where);
-       } else {
-               {
-                       Glib::RWLock::WriterLock lm (metrics_lock);
+       PropertyChanged (PropertyChange ());
+}
+
+void
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               MeterSection& first (first_meter());
+               if (ms.beat() != first.beat()) {
+                       remove_meter_locked (ms);
+                       add_meter_locked (meter, frame, true);
+               } else {
                        /* cannot move the first meter section */
-                       *((Meter*)&first) = meter;
+                       *static_cast<Meter*>(&first) = meter;
+                       recompute_map (true);
                }
-               recompute_map (true);
        }
 
        PropertyChanged (PropertyChange ());
 }
 
+
 void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
 {
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_meter_locked (meter, beat, where, true);
+       }
 
-               /* a new meter always starts a new bar on the first beat. so
-                  round the start time appropriately. remember that
-                  `where' is based on the existing tempo map, not
-                  the result after we insert the new meter.
 
-               */
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (std::cerr);
+       }
+#endif
 
-               if (where.beats != 1) {
-                       where.beats = 1;
-                       where.bars++;
-               }
+       PropertyChanged (PropertyChange ());
+}
 
-               /* new meters *always* start on a beat. */
-               where.ticks = 0;
-               
-               do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
+void
+TempoMap::add_meter (const Meter& meter, framepos_t frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_meter_locked (meter, frame, true);
        }
 
-       recompute_map (true);
-       
+
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
                dump (std::cerr);
@@ -586,19 +1087,68 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
        PropertyChanged (PropertyChange ());
 }
 
+void
+TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
+{
+       /* a new meter always starts a new bar on the first beat. so
+          round the start time appropriately. remember that
+          `where' is based on the existing tempo map, not
+          the result after we insert the new meter.
+
+       */
+
+       if (where.beats != 1) {
+               where.beats = 1;
+               where.bars++;
+       }
+
+       /* new meters *always* start on a beat. */
+       where.ticks = 0;
+
+       do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
+
+       if (recompute) {
+               recompute_map (true);
+       }
+
+}
+
+void
+TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
+{
+
+       /* MusicTime meters *always* start on 1|1|0. */
+       MeterSection* ms = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
+       BBT_Time bbt;
+       pair<double, BBT_Time> pr;
+       bbt.bars = 1;
+       bbt.beats = 1;
+       bbt.ticks = 0;
+       /* just a dummy - the actual beat should be applied in recompute_map() as thins is AudioTime */
+       pr.first = 0.0;
+       pr.second = bbt;
+       ms->set_beat (pr);
+       do_insert (ms);
+
+       if (recompute) {
+               recompute_map (true);
+       }
+
+}
+
 void
 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
        Tempo newtempo (beats_per_minute, note_type);
        TempoSection* t;
 
-       for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
                        {
-                               Glib::RWLock::WriterLock lm (metrics_lock);
+                               Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
+                               recompute_map (false);
                        }
-                       recompute_map (false);
                        PropertyChanged (PropertyChange ());
                        break;
                }
@@ -617,7 +1167,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
        /* find the TempoSection immediately preceding "where"
         */
 
-       for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
+       for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
 
                if ((*i)->frame() > where) {
                        break;
@@ -645,12 +1195,12 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
        /* reset */
 
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
+               Glib::Threads::RWLock::WriterLock lm (lock);
                /* cannot move the first tempo section */
                *((Tempo*)prev) = newtempo;
+               recompute_map (false);
        }
 
-       recompute_map (false);
        PropertyChanged (PropertyChange ());
 }
 
@@ -659,14 +1209,32 @@ TempoMap::first_meter () const
 {
        const MeterSection *m = 0;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
                        return *m;
                }
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
-       /*NOTREACHED*/
+       abort(); /*NOTREACHED*/
+       return *m;
+}
+
+MeterSection&
+TempoMap::first_meter ()
+{
+       MeterSection *m = 0;
+
+       /* CALLER MUST HOLD LOCK */
+
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       return *m;
+               }
+       }
+
+       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
        return *m;
 }
 
@@ -675,785 +1243,629 @@ TempoMap::first_tempo () const
 {
        const TempoSection *t = 0;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       /* CALLER MUST HOLD LOCK */
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
                        return *t;
                }
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
-       /*NOTREACHED*/
+       abort(); /*NOTREACHED*/
        return *t;
 }
 
-void
-TempoMap::timestamp_metrics_from_audio_time ()
+TempoSection&
+TempoMap::first_tempo ()
 {
-       Metrics::iterator i;
-       const MeterSection* meter;
-       const TempoSection* tempo;
-       MeterSection *m;
-       TempoSection *t;
+       TempoSection *t = 0;
 
-       meter = &first_meter ();
-       tempo = &first_tempo ();
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
+                       return *t;
+               }
+       }
 
-       BBT_Time start;
-       BBT_Time end;
-       
-       // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
+       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
+       return *t;
+}
 
-       bool first = true;
-       MetricSection* prev = 0;
+void
+TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
+{
+       /* CALLER MUST HOLD WRITE LOCK */
 
-       for (i = metrics->begin(); i != metrics->end(); ++i) {
+       if (end < 0) {
 
-               BBT_Time bbt;
-               TempoMetric metric (*meter, *tempo);
+               /* we will actually stop once we hit
+                  the last metric.
+               */
+               end = max_framepos;
 
-               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
-               }
+       }
 
-               BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
-               bbt_time_unlocked ((*i)->frame(), bbt, bi);
-               
-               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
-               if (first) {
-                       first = false;
-               } else {
+       if (end == 0) {
+               /* silly call from Session::process() during startup
+                */
+               return;
+       }
 
-                       if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
-                               /* round up to next beat */
-                               bbt.beats += 1;
-                       }
+       Metrics::const_iterator i;
+       TempoSection* prev_ts = 0;
 
-                       bbt.ticks = 0;
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-                       if (bbt.beats != 1) {
-                               /* round up to next bar */
-                               bbt.bars += 1;
-                               bbt.beats = 1;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t->position_lock_style() == AudioTime) {
+                                       if (prev_ts->type() == TempoSection::Ramp) {
+                                               prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame() - prev_ts->frame(), _frame_rate));
+                                               t->set_beat (prev_ts->beat_at_frame (t->frame() - prev_ts->frame(), _frame_rate) + prev_ts->beat());
+                                       } else {
+                                               prev_ts->set_c_func (0.0);
+                                               t->set_beat (prev_ts->beat_at_frame (t->frame() - prev_ts->frame(), _frame_rate) + prev_ts->beat());
+                                       }
+                               } else if (t->position_lock_style() == MusicTime) {
+                                       if (prev_ts->type() == TempoSection::Ramp) {
+                                               prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat() - prev_ts->beat(), _frame_rate);
+                                               t->set_frame (prev_ts->frame_at_beat (t->beat() - prev_ts->beat(), _frame_rate) + prev_ts->frame());
+                                       } else {
+                                               prev_ts->set_c_func (0.0);
+                                               t->set_frame (prev_ts->frame_at_beat (t->beat() - prev_ts->beat(), _frame_rate) + prev_ts->frame());
+                                       }
+                               }
                        }
+                       prev_ts = t;
                }
+       }
 
-               // cerr << bbt << endl;
+       MeterSection* meter = 0;
 
-               (*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*/
-               }
+       for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
+               /* We now have the tempo map set. use it to set meter positions.*/
+               if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
+                       if (meter->position_lock_style() == AudioTime) {
+                               /* a frame based meter has to have a 1|1|0 bbt */
+                               pair<double, BBT_Time> pr;
+                               BBT_Time where;
 
-               prev = (*i);
-       }
+                               where.bars = 1;
+                               where.beats = 1;
+                               where.ticks = 0;
 
-#ifndef NDEBUG
-       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (cerr);
+                               pr.first = tick_at_frame (meter->frame()) / BBT_Time::ticks_per_beat;
+                               pr.second = where;
+                               meter->set_beat (pr);
+                       } else if (meter->position_lock_style() == MusicTime) {
+                               meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
+                       }
+               }
        }
-#endif
-
 }
 
-void
-TempoMap::require_map_to (framepos_t pos)
+
+TempoMetric
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 {
-       bool revise_map;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
 
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               revise_map = (_map->empty() || _map->back().frame < pos);
-       }
+       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
+          at something, because we insert the default tempo and meter during
+          TempoMap construction.
 
-       if (revise_map) {
-               recompute_map (false, pos);
-       }
-}
+          now see if we can find better candidates.
+       */
 
-void
-TempoMap::require_map_to (const BBT_Time& bbt)
-{
-       bool revise_map;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               revise_map = (_map->empty() || _map->back().bbt() < bbt);
-       }
+               if ((*i)->frame() > frame) {
+                       break;
+               }
 
-       if (revise_map) {
-               recompute_map (false, 99);
+               m.set_metric(*i);
+
+               if (last) {
+                       *last = i;
+               }
        }
-}
 
-void
-TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
+       return m;
+}
+/* XX meters only */
+TempoMetric
+TempoMap::metric_at (BBT_Time bbt) const
 {
-       MeterSection* meter;
-       TempoSection* tempo;
-       TempoSection* ts;
-       MeterSection* ms;
-       double divisions_per_bar;
-       double beat_frames;
-       double current_frame;
-       BBT_Time current;
-       Metrics::iterator next_metric;
-       BBTPointList* new_map = new BBTPointList;
-
-       if (end < 0) {
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
 
-               Glib::RWLock::ReaderLock lm (map_lock);
+       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
+          at something, because we insert the default tempo and meter during
+          TempoMap construction.
 
-               if (_map->empty()) {
-                       /* compute 1 mins worth */
-                       end = _frame_rate * 60;
-               } else {
-                       end = _map->back().frame;
-               }
-       }
+          now see if we can find better candidates.
+       */
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* mw;
+               if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       BBT_Time section_start (mw->bbt());
 
-       Glib::RWLock::ReaderLock lm (metrics_lock);
-       
-       for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
-               if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       meter = ms;
-                       break;
-               }
-       }
+                       if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+                               break;
+                       }
 
-       for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
-               if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       tempo = ts;
-                       break;
+                       m.set_metric (*i);
                }
        }
 
-       /* assumes that the first meter & tempo are at frame zero */
-       current_frame = 0;
-       meter->set_frame (0);
-       tempo->set_frame (0);
-
-       /* assumes that the first meter & tempo are at 1|1|0 */
-       current.bars = 1;
-       current.beats = 1;
-       current.ticks = 0;
+       return m;
+}
 
-       divisions_per_bar = meter->divisions_per_bar ();
-       beat_frames = meter->frames_per_division (*tempo,_frame_rate);
-       
-       if (reassign_tempo_bbt) {
+void
+TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-               MeterSection* rmeter = meter;
+       if (frame < 0) {
+               bbt.bars = 1;
+               bbt.beats = 1;
+               bbt.ticks = 0;
+               warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
+               return;
+       }
+       bbt = beats_to_bbt_locked (beat_at_frame (frame));
+}
 
-               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
+double
+TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return bbt_to_beats_locked (bbt);
+}
 
-               for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
-                       
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+double
+TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
+{
+       /* CALLER HOLDS READ LOCK */
 
-                               /* reassign the BBT time of this tempo section
-                                * based on its bar offset position.
-                                */
+       double accumulated_beats = 0.0;
+       double accumulated_bars = 0.0;
+       MeterSection* prev_ms = 0;
 
-                               ts->update_bbt_time_from_bar_offset (*rmeter);
+       Metrics::const_iterator i;
 
-                       } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               rmeter = ms;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               /*NOTREACHED*/
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       double bars_to_m = 0.0;
+                       if (prev_ms) {
+                               bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
+                       }
+                       if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
+                               break;
                        }
+                       if (prev_ms) {
+                               accumulated_beats += m->beat() - prev_ms->beat();
+                               accumulated_bars += bars_to_m;
+                       }
+                       prev_ms = m;
                }
        }
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n", 
-                                                      *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
-
-       next_metric = metrics->begin();
-       ++next_metric; // skip meter (or tempo)
-       ++next_metric; // skip tempo (or meter)
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
-       new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
+       double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
+       double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
+       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+       return ret;
+}
 
-       while (current_frame < end) {
-               
-               current.beats++;
-               current_frame += beat_frames;
+Timecode::BBT_Time
+TempoMap::beats_to_bbt (double beats)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beats_to_bbt_locked (beats);
+}
 
-               if (current.beats > meter->divisions_per_bar()) {
-                       current.bars++;
-                       current.beats = 1;
-               }
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_locked (double beats)
+{
+       /* CALLER HOLDS READ LOCK */
 
-               if (next_metric != metrics->end()) {
+       MeterSection* prev_ms = 0;
+       uint32_t accumulated_bars = 0;
 
-                       /* no operator >= so invert operator < */
+       Metrics::const_iterator i;
 
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-                       if (!(current < (*next_metric)->start())) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                         set_metrics:
-                               if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
+                       if (beats < m->beat()) {
+                               /* this is the meter after the one our beat is on*/
+                               break;
+                       }
 
-                                       tempo = ts;
+                       if (prev_ms) {
+                               /* we need a whole number of bars. */
+                               accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
+                       }
 
-                                       /* new tempo section: if its on a beat,
-                                        * we don't have to do anything other
-                                        * than recompute various distances,
-                                        * done further below as we transition
-                                        * the next metric section.
-                                        *
-                                        * if its not on the beat, we have to
-                                        * compute the duration of the beat it
-                                        * is within, which will be different
-                                        * from the preceding following ones
-                                        * since it takes part of its duration
-                                        * from the preceding tempo and part 
-                                        * from this new tempo.
-                                        */
+                       prev_ms = m;
+               }
+       }
 
-                                       if (tempo->start().ticks != 0) {
-                                               
-                                               double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);                                      
-                                               
-                                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
-                                                                                              tempo->start(), current_frame, tempo->bar_offset()));
-                                               
-                                               /* back up to previous beat */
-                                               current_frame -= beat_frames;
-                                               /* set tempo section location based on offset from last beat */
-                                               tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
-                                               /* advance to the location of the new (adjusted) beat */
-                                               current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
-                                               /* next metric doesn't have to
-                                                * match this precisely to
-                                                * merit a reloop ...
-                                                */
-                                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
-                                               
-                                       } else {
-                                               
-                                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
-                                                                                              tempo->start(), current_frame));
-                                               tempo->set_frame (current_frame);
-                                       }
+       double const beats_in_ms = beats - prev_ms->beat();
+       uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
+       uint32_t const total_bars = bars_in_ms + accumulated_bars;
+       double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
+       double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
-                               } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
-                                       
-                                       meter = ms;
+       BBT_Time ret;
 
-                                       /* new meter section: always defines the
-                                        * start of a bar.
-                                        */
-                                       
-                                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
-                                                                                      meter->start(), current, current_frame));
-                                       
-                                       assert (current.beats == 1);
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-                                       meter->set_frame (current_frame);
-                               }
-                               
-                               divisions_per_bar = meter->divisions_per_bar ();
-                               beat_frames = meter->frames_per_division (*tempo, _frame_rate);
-                               
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", 
-                                                                              beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
-                       
-                               ++next_metric;
-
-                               if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
-                                       /* same position so go back and set this one up before advancing
-                                       */
-                                       goto set_metrics;
-                               }
-                       }
-               }
+       /* 0 0 0 to 1 1 0 - based mapping*/
+       ++ret.bars;
+       ++ret.beats;
 
-               if (current.beats == 1) {
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
-                       new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
-               } else {
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
-                       new_map->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
-               }
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       {
-               Glib::RWLock::WriterLock lm (map_lock);
-               swap (_map, new_map);
-               delete new_map;
+       if (ret.beats > prev_ms->divisions_per_bar()) {
+               ++ret.bars;
+               ret.beats = 1;
        }
+
+       return ret;
 }
 
-TempoMetric
-TempoMap::metric_at (framepos_t frame) const
+double
+TempoMap::tick_at_frame (framecnt_t frame) const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
-       TempoMetric m (first_meter(), first_tempo());
-       const Meter* meter;
-       const Tempo* tempo;
+       /* HOLD (at least) THE READER LOCK */
 
-       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
-          at something, because we insert the default tempo and meter during
-          TempoMap construction.
+       Metrics::const_iterator i;
+       TempoSection* prev_ts = 0;
+       double accumulated_ticks = 0.0;
 
-          now see if we can find better candidates.
-       */
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-               // cerr << "Looking at a metric section " << **i << endl;
+                       if ((prev_ts) && frame < t->frame()) {
+                               /*the previous ts is the one containing the frame */
 
-               if ((*i)->frame() > frame) {
-                       break;
-               }
+                               framepos_t const time = frame - prev_ts->frame();
 
-               if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       m.set_tempo (*tempo);
-               } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       m.set_meter (*meter);
-               }
+                               return prev_ts->tick_at_frame (time, _frame_rate) + accumulated_ticks;
+                       }
 
-               m.set_frame ((*i)->frame ());
-               m.set_start ((*i)->start ());
+                       if (prev_ts && t->frame() > prev_ts->frame()) {
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
+                       }
+
+                       prev_ts = t;
+               }
        }
-       
-       // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
-       return m;
+
+       /* treated as constant for this ts */
+       framecnt_t const frames_in_section = frame - prev_ts->frame();
+       double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+
+       return ticks_in_section + accumulated_ticks;
+
 }
 
-TempoMetric
-TempoMap::metric_at (BBT_Time bbt) const
+framecnt_t
+TempoMap::frame_at_tick (double tick) const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
-       TempoMetric m (first_meter(), first_tempo());
-       const Meter* meter;
-       const Tempo* tempo;
+       /* HOLD THE READER LOCK */
 
-       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
-          at something, because we insert the default tempo and meter during
-          TempoMap construction.
+       double accumulated_ticks = 0.0;
+       double accumulated_ticks_to_prev = 0.0;
+       const TempoSection* prev_ts = 0;
 
-          now see if we can find better candidates.
-       */
+       Metrics::const_iterator i;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-               BBT_Time section_start ((*i)->start());
+                       if (prev_ts && t->frame() > prev_ts->frame()) {
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
+                       }
 
-               if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
-                       break;
-               }
+                       if (prev_ts && tick < accumulated_ticks) {
+                               /* prev_ts is the one affecting us. */
 
-               if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       m.set_tempo (*tempo);
-               } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       m.set_meter (*meter);
-               }
+                               double const ticks_in_section = tick - accumulated_ticks_to_prev;
 
-               m.set_frame ((*i)->frame ());
-               m.set_start (section_start);
+                               return prev_ts->frame_at_tick (ticks_in_section, _frame_rate) + prev_ts->frame();
+                       }
+                       accumulated_ticks_to_prev = accumulated_ticks;
+                       prev_ts = t;
+               }
        }
+       /* must be treated as constant, irrespective of _type */
+       double const ticks_in_section = tick - accumulated_ticks_to_prev;
+       double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
 
-       return m;
+       framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+
+       return ret;
 }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
+double
+TempoMap::beat_at_frame (framecnt_t frame) const
 {
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               BBTPointList::const_iterator i = bbt_before_or_at (frame);
-               bbt_time_unlocked (frame, bbt, i);
-       }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
 }
 
-void
-TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
+framecnt_t
+TempoMap::frame_at_beat (double beat) const
 {
-       bbt.bars = (*i).bar;
-       bbt.beats = (*i).beat;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       if ((*i).frame == frame) {
-               bbt.ticks = 0;
-       } else {
-               bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
-                                   BBT_Time::ticks_per_bar_division);
-       }
+       return frame_at_tick (beat * BBT_Time::ticks_per_beat);
 }
 
 framepos_t
 TempoMap::frame_time (const BBT_Time& bbt)
 {
-       Glib::RWLock::ReaderLock lm (map_lock);
-
-       BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
-       BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
-
-       if (bbt.ticks != 0) {
-               return ((*e).frame - (*s).frame) + 
-                       llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
-       } else {
-               return ((*e).frame - (*s).frame);
+       if (bbt.bars < 1) {
+               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
+               return 0;
        }
-}
 
-framecnt_t
-TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
-{
-       Glib::RWLock::ReaderLock lm (map_lock);
-       framecnt_t frames = 0;
-       BBT_Time when;
+       if (bbt.beats < 1) {
+               throw std::logic_error ("beats are counted from one");
+       }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       bbt_time (pos, when);
-       frames = bbt_duration_at_unlocked (when, bbt,dir);
+       framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
 
-       return frames;
+       return ret;
 }
 
+
 framecnt_t
-TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) 
+TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
-       if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
-               return 0;
-       }
 
-       /* round back to the previous precise beat */
-       BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
-       BBTPointList::const_iterator start (wi);
-       double tick_frames = 0;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       assert (wi != _map->end());
+       Metrics::const_iterator i;
+       TempoSection* first = 0;
+       TempoSection* second = 0;
 
-       /* compute how much rounding we did because of non-zero ticks */
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       if (when.ticks != 0) {
-               tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
-       }
-       
-       uint32_t bars = 0;
-       uint32_t beats = 0;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-       while (wi != _map->end() && bars < bbt.bars) {
-               ++wi;
-               if ((*wi).is_bar()) {
-                       ++bars;
-               }
-       }
-       assert (wi != _map->end());
+                       if ((*i)->frame() > pos) {
+                               second = t;
+                               break;
+                       }
 
-       while (wi != _map->end() && beats < bbt.beats) {
-               ++wi;
-               ++beats;
+                       first = t;
+               }
        }
-       assert (wi != _map->end());
+       if (first && second) {
+               framepos_t const time = pos - first->frame();
+               double const tick_at_time = first->tick_at_frame (time, _frame_rate);
+               double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
+               double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
 
-       /* add any additional frames related to ticks in the added value */
-
-       if (bbt.ticks != 0) {
-               tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
+               return time_at_bbt - time;
        }
 
-       return ((*wi).frame - (*start).frame) + llrint (tick_frames);
+       double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
+       return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
 }
 
 framepos_t
-TempoMap::round_to_bar (framepos_t fr, int dir)
+TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Bar);
 }
 
 framepos_t
-TempoMap::round_to_beat (framepos_t fr, int dir)
+TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Beat);
 }
 
 framepos_t
-TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
+TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
-       Glib::RWLock::ReaderLock lm (map_lock);
-       BBTPointList::const_iterator i = bbt_before_or_at (fr);
-       BBT_Time the_beat;
-       uint32_t ticks_one_subdivisions_worth;
-       uint32_t difference;
-
-       bbt_time_unlocked (fr, the_beat, i);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
-                                                    fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
+       uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
+       uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
+       uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
 
-       ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
+       ticks -= beats * BBT_Time::ticks_per_beat;
 
        if (dir > 0) {
+               /* round to next (or same iff dir == RoundUpMaybe) */
 
-               /* round to next (even if we're on a subdivision */
+               uint32_t mod = ticks % ticks_one_subdivisions_worth;
 
-               uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
+               if (mod == 0 && dir == RoundUpMaybe) {
+                       /* right on the subdivision, which is fine, so do nothing */
 
-               if (mod == 0) {
+               } else if (mod == 0) {
                        /* right on the subdivision, so the difference is just the subdivision ticks */
-                       the_beat.ticks += ticks_one_subdivisions_worth;
+                       ticks += ticks_one_subdivisions_worth;
 
                } else {
                        /* not on subdivision, compute distance to next subdivision */
 
-                       the_beat.ticks += ticks_one_subdivisions_worth - mod;
+                       ticks += ticks_one_subdivisions_worth - mod;
                }
 
-               if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
-                       assert (i != _map->end());
-                       ++i;
-                       assert (i != _map->end());
-                       the_beat.ticks -= BBT_Time::ticks_per_bar_division;
-               } 
-
-
+               if (ticks >= BBT_Time::ticks_per_beat) {
+                       ticks -= BBT_Time::ticks_per_beat;
+               }
        } else if (dir < 0) {
 
-               /* round to previous (even if we're on a subdivision) */
+               /* round to previous (or same iff dir == RoundDownMaybe) */
 
-               uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
+               uint32_t difference = ticks % ticks_one_subdivisions_worth;
 
-               if (mod == 0) {
-                       /* right on the subdivision, so the difference is just the subdivision ticks */
+               if (difference == 0 && dir == RoundDownAlways) {
+                       /* right on the subdivision, but force-rounding down,
+                          so the difference is just the subdivision ticks */
                        difference = ticks_one_subdivisions_worth;
-               } else {
-                       /* not on subdivision, compute distance to previous subdivision, which
-                          is just the modulus.
-                       */
-
-                       difference = mod;
                }
 
-               if (the_beat.ticks < difference) {
-                       if (i == _map->begin()) {
-                               /* can't go backwards from wherever pos is, so just return it */
-                               return fr;
-                       }
-                       --i;
-                       the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
+               if (ticks < difference) {
+                       ticks = BBT_Time::ticks_per_beat - ticks;
                } else {
-                       the_beat.ticks -= difference;
+                       ticks -= difference;
                }
 
        } else {
                /* round to nearest */
-
                double rem;
 
                /* compute the distance to the previous and next subdivision */
-               
-               if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
-                       
+
+               if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
+
                        /* closer to the next subdivision, so shift forward */
 
-                       the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
+                       ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
 
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
 
-                       if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
-                               assert (i != _map->end());
-                               ++i;
-                               assert (i != _map->end());
-                               the_beat.ticks -= BBT_Time::ticks_per_bar_division;
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
-                       } 
+                       if (ticks > BBT_Time::ticks_per_beat) {
+                               ++beats;
+                               ticks -= BBT_Time::ticks_per_beat;
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
+                       }
 
                } else if (rem > 0) {
-                       
+
                        /* closer to previous subdivision, so shift backward */
 
-                       if (rem > the_beat.ticks) {
-                               if (i == _map->begin()) {
+                       if (rem > ticks) {
+                               if (beats == 0) {
                                        /* can't go backwards past zero, so ... */
                                        return 0;
                                }
                                /* step back to previous beat */
-                               --i;
-                               the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
+                               --beats;
+                               ticks = lrint (BBT_Time::ticks_per_beat - rem);
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
                        } else {
-                               the_beat.ticks = lrint (the_beat.ticks - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
+                               ticks = lrint (ticks - rem);
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
                        }
                } else {
                        /* on the subdivision, do nothing */
                }
        }
-
-       return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) * 
-               (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
+       return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
 }
 
 framepos_t
-TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
+TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
-       Glib::RWLock::ReaderLock lm (map_lock);
-       BBTPointList::const_iterator fi;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       if (dir > 0) {
-               fi = bbt_after_or_at (frame);
-       } else {
-               fi = bbt_before_or_at (frame);
-       }
+       double const beat_at_framepos = beat_at_frame (frame);
 
-       assert (fi != _map->end());
+       BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
 
-       DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
-               
        switch (type) {
        case Bar:
                if (dir < 0) {
                        /* find bar previous to 'frame' */
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
 
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               --fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               if (fi == _map->begin()) {
-                                       break;
-                               }
-                               fi--;
-                       }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
-
-               } else if (dir > 0) {
-
-                       /* find bar following 'frame' */
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               ++fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               fi++;
-                               if (fi == _map->end()) {
-                                       --fi;
-                                       break;
-                               }
-                       }
-
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
-
-               } else {
-                       
-                       /* true rounding: find nearest bar */
-
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
-
-                       while ((*prev).beat != 1) {
-                               if (prev == _map->begin()) {
-                                       break;
-                               }
-                               prev--;
-                       }
-
-                       while ((*next).beat != 1) {
-                               next++;
-                               if (next == _map->end()) {
-                                       --next;
-                                       break;
-                               }
-                       }
-
-                       if ((frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
+               } else if (dir > 0) {
+                       /* find bar following 'frame' */
+                       ++bbt.bars;
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
+               } else {
+                       /* true rounding: find nearest bar */
+                       framepos_t raw_ft = frame_time (bbt);
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       framepos_t prev_ft = frame_time (bbt);
+                       ++bbt.bars;
+                       framepos_t next_ft = frame_time (bbt);
+
+                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
+                               return next_ft;
                        } else {
-                               return (*next).frame;
+                               return prev_ft;
                        }
-                       
                }
 
                break;
 
        case Beat:
                if (dir < 0) {
-                       if ((*fi).frame >= frame) {
-                               DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
-                               --fi;
-                       }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
+                       return frame_at_beat (floor (beat_at_framepos));
                } else if (dir > 0) {
-                       if ((*fi).frame <= frame) {
-                               DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
-                               ++fi;
-                       }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
+                       return frame_at_beat (ceil (beat_at_framepos));
                } else {
-                       /* find beat nearest to frame */
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
-
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-                       --prev;
-                       ++next;
-                       
-                       if ((frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
-                       } else {
-                               return (*next).frame;
-                       }
+                       return frame_at_beat (floor (beat_at_framepos + 0.5));
                }
                break;
        }
 
-       /* NOTREACHED */
-       assert (false);
        return 0;
 }
 
 void
-TempoMap::map (TempoMap::BBTPointList::const_iterator& begin, 
-              TempoMap::BBTPointList::const_iterator& end, 
-              framepos_t lower, framepos_t upper) 
+TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
+                   framepos_t lower, framepos_t upper)
 {
-       require_map_to (upper);
-       begin = lower_bound (_map->begin(), _map->end(), lower);
-       end = upper_bound (_map->begin(), _map->end(), upper);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
+       uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
+
+       while (cnt <= upper_beat) {
+               framecnt_t const pos = frame_at_beat (cnt);
+               MeterSection const meter = meter_section_at (pos);
+               Tempo const tempo = tempo_at (pos);
+               BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
+
+               points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
+               ++cnt;
+       }
 }
 
 const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
        Metrics::const_iterator i;
        TempoSection* prev = 0;
 
-       for (i = metrics->begin(); i != metrics->end(); ++i) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
@@ -1468,18 +1880,98 @@ TempoMap::tempo_section_at (framepos_t frame) const
 
        if (prev == 0) {
                fatal << endmsg;
+               abort(); /*NOTREACHED*/
        }
 
        return *prev;
 }
 
-const Tempo&
+/* don't use this to calculate length (the tempo is only correct for this frame).
+   do that stuff based on the beat_at_frame and frame_at_beat api
+*/
+double
+TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       const TempoSection* ts_at = &tempo_section_at (frame);
+       const TempoSection* ts_after = 0;
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if ((*i)->frame() > frame) {
+                               ts_after = t;
+                               break;
+                       }
+               }
+       }
+
+       if (ts_after) {
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), _frame_rate));
+       }
+       /* must be treated as constant tempo */
+       return ts_at->frames_per_beat (_frame_rate);
+}
+
+const Tempo
 TempoMap::tempo_at (framepos_t frame) const
 {
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
        TempoMetric m (metric_at (frame));
+       TempoSection* prev_ts = 0;
+
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if ((prev_ts) && t->frame() > frame) {
+                               /* this is the one past frame */
+                               framepos_t const time = frame - prev_ts->frame();
+                               double const ret = prev_ts->tempo_at_frame (time, _frame_rate);
+                               Tempo const ret_tempo (ret, m.tempo().note_type ());
+                               return ret_tempo;
+                       }
+                       prev_ts = t;
+               }
+       }
+
        return m.tempo();
+
 }
 
+const MeterSection&
+TempoMap::meter_section_at (framepos_t frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics::const_iterator i;
+       MeterSection* prev = 0;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* t;
+
+               if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
+
+                       if ((*i)->frame() > frame) {
+                               break;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       return *prev;
+}
 
 const Meter&
 TempoMap::meter_at (framepos_t frame) const
@@ -1495,8 +1987,8 @@ TempoMap::get_state ()
        XMLNode *root = new XMLNode ("TempoMap");
 
        {
-               Glib::RWLock::ReaderLock lm (metrics_lock);
-               for (i = metrics->begin(); i != metrics->end(); ++i) {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               for (i = metrics.begin(); i != metrics.end(); ++i) {
                        root->add_child_nocopy ((*i)->get_state());
                }
        }
@@ -1508,17 +2000,16 @@ int
 TempoMap::set_state (const XMLNode& node, int /*version*/)
 {
        {
-               Glib::RWLock::WriterLock lm (metrics_lock);
+               Glib::Threads::RWLock::WriterLock lm (lock);
 
                XMLNodeList nlist;
                XMLNodeConstIterator niter;
-               Metrics old_metrics (*metrics);
+               Metrics old_metrics (metrics);
                MeterSection* last_meter = 0;
-
-               metrics->clear();
+               metrics.clear();
 
                nlist = node.children();
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
@@ -1526,18 +2017,18 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        TempoSection* ts = new TempoSection (*child);
-                                       metrics->push_back (ts);
+                                       metrics.push_back (ts);
 
                                        if (ts->bar_offset() < 0.0) {
                                                if (last_meter) {
-                                                       ts->update_bar_offset_from_bbt (*last_meter);
-                                               } 
+                                                       //ts->update_bar_offset_from_bbt (*last_meter);
+                                               }
                                        }
                                }
 
                                catch (failed_constructor& err){
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       *metrics = old_metrics;
+                                       metrics = old_metrics;
                                        break;
                                }
 
@@ -1545,13 +2036,13 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        MeterSection* ms = new MeterSection (*child);
-                                       metrics->push_back (ms);
+                                       metrics.push_back (ms);
                                        last_meter = ms;
                                }
 
                                catch (failed_constructor& err) {
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       *metrics = old_metrics;
+                                       metrics = old_metrics;
                                        break;
                                }
                        }
@@ -1559,11 +2050,57 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                if (niter == nlist.end()) {
                        MetricSectionSorter cmp;
-                       metrics->sort (cmp);
+                       metrics.sort (cmp);
+               }
+               /* check for legacy sessions where bbt was the base musical unit for tempo */
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       MeterSection* prev_ms;
+                       TempoSection* prev_ts;
+                       if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               if (prev_ms->beat() < 0.0) {
+                                       /*XX we cannot possibly make this work??. */
+                                       pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
+                                       prev_ms->set_beat (start);
+                               }
+                       } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               if (prev_ts->beat() < 0.0) {
+                                       double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
+                                       prev_ts->set_beat (start);
+
+                               }
+                       }
+               }
+               /* check for multiple tempo/meters at the same location, which
+                  ardour2 somehow allowed.
+               */
+
+               Metrics::iterator prev = metrics.end();
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       if (prev != metrics.end()) {
+                               MeterSection* ms;
+                               MeterSection* prev_ms;
+                               TempoSection* ts;
+                               TempoSection* prev_ts;
+                               if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (prev_ms->beat() == ms->beat()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
+                                               return -1;
+                                       }
+                               } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                                       if (prev_ts->beat() == ts->beat()) {
+                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
+                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
+                                               return -1;
+                                       }
+                               }
+                       }
+                       prev = i;
                }
+
+               recompute_map (true, -1);
        }
 
-       recompute_map (true);
        PropertyChanged (PropertyChange ());
 
        return 0;
@@ -1572,17 +2109,17 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 void
 TempoMap::dump (std::ostream& o) const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
+       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
        const MeterSection* m;
        const TempoSection* t;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       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? "
+                       o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " 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()
+                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
                          << " (movable? " << m->movable() << ')' << endl;
                }
        }
@@ -1591,10 +2128,10 @@ TempoMap::dump (std::ostream& o) const
 int
 TempoMap::n_tempos() const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (dynamic_cast<const TempoSection*>(*i) != 0) {
                        cnt++;
                }
@@ -1606,10 +2143,10 @@ TempoMap::n_tempos() const
 int
 TempoMap::n_meters() const
 {
-       Glib::RWLock::ReaderLock lm (metrics_lock);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (dynamic_cast<const MeterSection*>(*i) != 0) {
                        cnt++;
                }
@@ -1621,315 +2158,399 @@ TempoMap::n_meters() const
 void
 TempoMap::insert_time (framepos_t where, framecnt_t amount)
 {
-       Glib::RWLock::WriterLock lm (metrics_lock);
-       for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
-               if ((*i)->frame() >= where && (*i)->movable ()) {
-                       (*i)->set_frame ((*i)->frame() + amount);
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       if ((*i)->frame() >= where && (*i)->movable ()) {
+                               (*i)->set_frame ((*i)->frame() + amount);
+                       }
+               }
+
+               /* now reset the BBT time of all metrics, based on their new
+                * audio time. This is the only place where we do this reverse
+                * timestamp.
+                */
+
+               Metrics::iterator i;
+               const MeterSection* meter;
+               const TempoSection* tempo;
+               MeterSection *m;
+               TempoSection *t;
+
+               meter = &first_meter ();
+               tempo = &first_tempo ();
+
+               BBT_Time start;
+               BBT_Time end;
+
+               // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
+
+               bool first = true;
+               MetricSection* prev = 0;
+
+               for (i = metrics.begin(); i != metrics.end(); ++i) {
+
+                       BBT_Time bbt;
+                       //TempoMetric metric (*meter, *tempo);
+                       MeterSection* ms = const_cast<MeterSection*>(meter);
+                       TempoSection* ts = const_cast<TempoSection*>(tempo);
+                       if (prev) {
+                               if (ts){
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               ts->set_beat (t->beat());
+                                       }
+                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+                                               ts->set_beat (m->beat());
+                                       }
+                                       ts->set_frame (prev->frame());
+
+                               }
+                               if (ms) {
+                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+                                               pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
+                                               ms->set_beat (start);
+                                       }
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
+                                               ms->set_beat (start);
+                                       }
+                                       ms->set_frame (prev->frame());
+                               }
+
+                       } else {
+                               // metric will be at frames=0 bbt=1|1|0 by default
+                               // which is correct for our purpose
+                       }
+
+                       // cerr << bbt << endl;
+
+                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               t->set_beat (beat_at_frame (m->frame()));
+                               tempo = t;
+                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               bbt_time (m->frame(), bbt);
+
+                               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
+
+                               if (first) {
+                                       first = false;
+                               } else {
+
+                                       if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
+                                               /* round up to next beat */
+                                               bbt.beats += 1;
+                                       }
+
+                                       bbt.ticks = 0;
+
+                                       if (bbt.beats != 1) {
+                                               /* round up to next bar */
+                                               bbt.bars += 1;
+                                               bbt.beats = 1;
+                                       }
+                               }
+                               pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
+                               m->set_beat (start);
+                               meter = m;
+                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+                       } else {
+                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
+                               abort(); /*NOTREACHED*/
+                       }
+
+                       prev = (*i);
                }
+
+               recompute_map (true);
        }
 
-       timestamp_metrics_from_audio_time ();
 
        PropertyChanged (PropertyChange ());
 }
+bool
+TempoMap::remove_time (framepos_t where, framecnt_t amount)
+{
+       bool moved = false;
+
+       std::list<MetricSection*> metric_kill_list;
+
+       TempoSection* last_tempo = NULL;
+       MeterSection* last_meter = NULL;
+       bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
+       bool meter_after = false; // is there a meter marker likewise?
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
+                               metric_kill_list.push_back(*i);
+                               TempoSection *lt = dynamic_cast<TempoSection*> (*i);
+                               if (lt)
+                                       last_tempo = lt;
+                               MeterSection *lm = dynamic_cast<MeterSection*> (*i);
+                               if (lm)
+                                       last_meter = lm;
+                       }
+                       else if ((*i)->frame() >= where) {
+                               // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
+                               (*i)->set_frame ((*i)->frame() - amount);
+                               if ((*i)->frame() == where) {
+                                       // marker was immediately after end of range
+                                       tempo_after = dynamic_cast<TempoSection*> (*i);
+                                       meter_after = dynamic_cast<MeterSection*> (*i);
+                               }
+                               moved = true;
+                       }
+               }
+
+               //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
+               if (last_tempo && !tempo_after) {
+                       metric_kill_list.remove(last_tempo);
+                       last_tempo->set_frame(where);
+                       moved = true;
+               }
+               if (last_meter && !meter_after) {
+                       metric_kill_list.remove(last_meter);
+                       last_meter->set_frame(where);
+                       moved = true;
+               }
+
+               //remove all the remaining metrics
+               for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
+                       metrics.remove(*i);
+                       moved = true;
+               }
+
+               if (moved) {
+                       recompute_map (true);
+               }
+       }
+       PropertyChanged (PropertyChange ());
+       return moved;
+}
 
 /** Add some (fractional) beats to a session frame position, and return the result in frames.
  *  pos can be -ve, if required.
  */
 framepos_t
-TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       return framepos_plus_bbt (pos, BBT_Time (beats));
+       return frame_at_beat (beat_at_frame (pos) + beats.to_double());
 }
 
-/** Subtract some (fractional) beats to a frame position, and return the result in frames */
+/** Subtract some (fractional) beats from a frame position, and return the result in frames */
 framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
+TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       return framepos_minus_bbt (pos, BBT_Time (beats));
+       return frame_at_beat (beat_at_frame (pos) - beats.to_double());
 }
 
+/** Add the BBT interval op to pos and return the result */
 framepos_t
-TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
+TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
-       Glib::RWLock::ReaderLock lm (map_lock);
-       BBTPointList::const_iterator i;
-       framecnt_t extra_frames = 0;
-       bool had_bars = (op.bars != 0);
+       cerr << "framepos_plus_bbt - untested" << endl;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       Metrics::const_iterator i;
+       const MeterSection* meter;
+       const MeterSection* m;
+       const TempoSection* tempo;
+       const TempoSection* next_tempo = 0;
+       const TempoSection* t;
+       double frames_per_beat;
+       framepos_t effective_pos = max (pos, (framepos_t) 0);
 
-       /* start from the bar|beat right before (or at) pos */
+       meter = &first_meter ();
+       tempo = &first_tempo ();
 
-       i = bbt_before_or_at (pos);
-       
-       /* we know that (*i).frame is less than or equal to pos */
-       extra_frames = pos - (*i).frame;
-       
-       /* walk backwards */
+       assert (meter);
+       assert (tempo);
 
-       while (i != _map->begin() && (op.bars || op.beats)) {
-               --i;
+       /* find the starting metrics for tempo & meter */
 
-               if (had_bars) {
-                       if ((*i).is_bar()) {
-                               if (op.bars) {
-                                       op.bars--;
-                               }
-                       }
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+
+               if ((*i)->frame() > effective_pos) {
+                       break;
                }
 
-               if ((had_bars && op.bars == 0) || !had_bars) {
-                       /* finished counting bars, or none to count, 
-                          so decrement beat count
-                       */
-                       if (op.beats) {
-                               op.beats--;
-                       }
+               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                       tempo = t;
+               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+                       meter = m;
                }
        }
-       
-       /* handle ticks (assumed to be less than
-        * BBT_Time::ticks_per_bar_division, as always.
-        */
 
-       if (op.ticks) {
-               frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
-               framepos_t pre_tick_frames = (*i).frame + extra_frames;
-               if (tick_frames < pre_tick_frames) {
-                       return pre_tick_frames - tick_frames;
-               } 
-               return 0;
-       } else {
-               return (*i).frame + extra_frames;
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((*i)->frame() > effective_pos) {
+                       if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                               next_tempo = t;
+                       }
+                       break;
+               }
        }
-}
 
-/** Add the BBT interval op to pos and return the result */
-framepos_t
-TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
-{
-       Glib::RWLock::ReaderLock lm (map_lock);
-       BBT_Time op_copy (op);
-       int additional_minutes = 1;
-       BBTPointList::const_iterator i;
-       framecnt_t backup_frames = 0;
-       bool had_bars = (op.bars != 0);
-               
-       while (true) {
+       /* We now have:
+
+          meter -> the Meter for "pos"
+          tempo -> the Tempo for "pos"
+          next_tempo -> the Tempo after "pos" or 0
+          i     -> for first new metric after "pos", possibly metrics.end()
+       */
 
-               i = bbt_before_or_at (pos);
+       /* now comes the complicated part. we have to add one beat a time,
+          checking for a new metric on every beat.
+       */
 
-               op = op_copy;
+       uint64_t bars = 0;
+       /* fpb is used for constant tempo */
+       frames_per_beat = tempo->frames_per_beat (_frame_rate);
 
-               /* we know that (*i).frame is before or equal to pos */
-               backup_frames = pos - (*i).frame;
+       while (op.bars) {
 
-               while (i != _map->end() && (op.bars || op.beats)) {
+               bars++;
+               op.bars--;
 
-                       ++i;
+               /* check if we need to use a new metric section: has adding frames moved us
+                  to or after the start of the next metric section? in which case, use it.
+               */
 
-                       if (had_bars) {
-                               if ((*i).is_bar()) {
-                                       if (op.bars) {
-                                               op.bars--;
-                                       }
+               if (i != metrics.end()) {
+                       if ((*i)->frame() <= pos) {
+
+                               /* about to change tempo or meter, so add the
+                                * number of frames for the bars we've just
+                                * traversed before we change the
+                                * frames_per_beat value.
+                                */
+
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       next_tempo = t;
                                }
-                       }
-                       
-                       if ((had_bars && op.bars == 0) || !had_bars) {
-                               /* finished counting bars, or none to count, 
-                                  so decrement beat count
-                               */
-
-                               if (op.beats) {
-                                       op.beats--;
+
+                               if (next_tempo) {
+                                       pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
+                               } else {
+                                       pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
                                }
-                       }
-               }
-               
-               if (i != _map->end()) {
-                       break;
-               }
 
-               /* we hit the end of the map before finish the bbt walk.
-                */
+                               bars = 0;
 
-               require_map_to (pos + (_frame_rate * 60 * additional_minutes));
-               additional_minutes *= 2;
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       tempo = t;
+                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+                                       meter = m;
+                               }
+                               ++i;
+                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
+                       }
+               }
 
-               /* go back and try again */
-               warning << "reached end of map with op now at " << op << " end = " 
-                       << _map->back().frame << ' ' << _map->back().bar << '|' << _map->back().beat << ", trying to walk " 
-                       << op_copy << " ... retry" 
-                       << endmsg;
        }
-       
-       if (op.ticks) {
-               return (*i).frame - backup_frames + 
-                       llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
+
+       if (next_tempo) {
+               pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
        } else {
-               return (*i).frame - backup_frames;
+               pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
        }
-}
-
-/** Count the number of beats that are equivalent to distance when going forward,
-    starting at pos.
-*/
-Evoral::MusicalTime
-TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
-{
-       Glib::RWLock::ReaderLock lm (map_lock);
-       BBTPointList::const_iterator i = bbt_after_or_at (pos);
-       Evoral::MusicalTime beats = 0;
-       framepos_t end = pos + distance;
 
-       require_map_to (end);
+       uint64_t beats = 0;
 
-       /* if our starting BBTPoint is after pos, add a fractional beat
-          to represent that distance.
-       */
+       while (op.beats) {
 
-       if ((*i).frame != pos) {
-               beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
-       }
+               /* given the current meter, have we gone past the end of the bar ? */
 
-       while (i != _map->end() && (*i).frame < end) {
-               ++i;
                beats++;
-       }
-       assert (i != _map->end());
-       
-       /* if our ending BBTPoint is after the end, subtract a fractional beat
-          to represent that distance.
-       */
-
-       if ((*i).frame > end) {
-               beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
-       }
-
-       return beats;
-}
+               op.beats--;
 
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_before_or_at (framepos_t pos)
-{
-       BBTPointList::const_iterator i;
+               /* check if we need to use a new metric section: has adding frames moved us
+                  to or after the start of the next metric section? in which case, use it.
+               */
 
-       require_map_to (pos);
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               i = lower_bound (_map->begin(), _map->end(), pos);
-               assert (i != _map->end());
-               if ((*i).frame > pos) {
-                       assert (i != _map->begin());
-                       --i;
-               }
-       }
-       return i;
-}
+               if (i != metrics.end()) {
+                       if ((*i)->frame() <= pos) {
 
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_after_or_at (framepos_t pos)
-{
-       BBTPointList::const_iterator i;
+                               /* about to change tempo or meter, so add the
+                                * number of frames for the beats we've just
+                                * traversed before we change the
+                                * frames_per_beat value.
+                                */
 
-       require_map_to (pos);
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               i = upper_bound (_map->begin(), _map->end(), pos);
-               assert (i != _map->end());
-       }
-       return i;
-}
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       next_tempo = t;
+                               }
 
-struct bbtcmp {
-    bool operator() (const BBT_Time& a, const BBT_Time& b) {
-           return a < b;
-    }
-};
+                               if (next_tempo) {
+                                       pos += tempo->frame_at_beat (beats, _frame_rate);
+                               } else {
+                                       pos += llrint (beats * frames_per_beat);
+                               }
 
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_point_for (const BBT_Time& bbt)
-{
-       BBTPointList::const_iterator i;
-       bbtcmp cmp;
-       int additional_minutes = 1;
+                               beats = 0;
 
-       while (1) {
-               {
-                       Glib::RWLock::ReaderLock lm (map_lock);
-                       if (!_map->empty() && _map->back().bar >= (bbt.bars + 1)) {
-                               break;
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       tempo = t;
+                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+                                       meter = m;
+                               }
+                               ++i;
+                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
                        }
                }
-               /* add some more distance, using bigger steps each time */
-               require_map_to (_map->back().frame + (_frame_rate * 60 * additional_minutes));
-               additional_minutes *= 2;
        }
 
-       {
-               Glib::RWLock::ReaderLock lm (map_lock);
-               i = lower_bound (_map->begin(), _map->end(), bbt, cmp);
-               assert (i != _map->end());
+       if (next_tempo) {
+               pos += tempo->frame_at_beat (beats, _frame_rate);
+       } else {
+               pos += llrint (beats * frames_per_beat);
        }
 
-       return i;
-}
-
-
-/** Compare the time of this with that of another MetricSection.
- *  @param with_bbt True to compare using start(), false to use frame().
- *  @return -1 for less than, 0 for equal, 1 for greater than.
- */
-
-int
-MetricSection::compare (const MetricSection& other) const
-{
-       if (start() == other.start()) {
-               return 0;
-       } else if (start() < other.start()) {
-               return -1;
-       } else {
-               return 1;
+       if (op.ticks) {
+               pos += tempo->frame_at_tick (op.ticks, _frame_rate);
        }
 
-       /* NOTREACHED */
-       return 0;
-}
+       return pos;
 
-bool
-MetricSection::operator== (const MetricSection& other) const
-{
-       return compare (other) == 0;
 }
 
-bool
-MetricSection::operator!= (const MetricSection& other) const
+/** Count the number of beats that are equivalent to distance when going forward,
+    starting at pos.
+*/
+Evoral::Beats
+TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
-       return compare (other) != 0;
+       return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
 }
 
-std::ostream& 
+struct bbtcmp {
+    bool operator() (const BBT_Time& a, const BBT_Time& b) {
+           return a < b;
+    }
+};
+
+std::ostream&
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const Tempo& t) {
        return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const MetricSection& section) {
 
-       o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
+       o << "MetricSection @ " << section.frame() << ' ';
 
        const TempoSection* ts;
        const MeterSection* ms;
 
        if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
-               o << *((Tempo*) ts);
+               o << *((const Tempo*) ts);
        } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
-               o << *((Meter*) ms);
+               //o << *((const Meter*) ms);
        }
 
        return o;