Tempo ramps - change some prototypes, rename some methods.
[ardour.git] / libs / ardour / tempo.cc
index 2f29a0dabf059807889371b01e21e7151d559003..5463d8539b2064565b728a4c1707f1ddb11f4570 100644 (file)
 #include <unistd.h>
 
 #include <glibmm/threads.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"
@@ -46,11 +49,11 @@ Tempo    TempoMap::_default_tempo (120.0);
 
 /***********************************************************************/
 
-double 
+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 
+          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".
@@ -65,31 +68,49 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
        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_("C"));
-
-       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_("C"));
+       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,381 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / 
+       _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, start(), m.divisions_per_bar()));
+       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 session) frame.
+*/
+double
+TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
+{
+
+       if (_type == Constant) {
+               return beats_per_minute();
+       }
+
+       return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based frame (relative to session)
+   where the tempo occurs in this section.
+   beat b is only used for constant tempos.
+   note that the tempo map may have multiple such values.
+*/
+framepos_t
+TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return ((b - beat())  * frames_per_beat (frame_rate))  + frame();
+       }
+
+       return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate) + frame();
+}
+/** returns the tempo at the zero-based (relative to session) beat.
+*/
+double
+TempoSection::tempo_at_beat (double b) const
+{
+
+       if (_type == Constant) {
+               return beats_per_minute();
+       }
+
+       return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based beat (relative to session)
+   where the tempo occurs given frame f. frame f is only used for constant tempos.
+   note that the session tempo map may have multiple beats at a given tempo.
+*/
+double
+TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
+               return ticks / BBT_Time::ticks_per_beat;
+       }
+
+       return (tick_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based beat (relative to session origin)
+   where the zero-based frame (relative to session)
+   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 session start frame)
+   where the zero-based beat (relative to session 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);
+}
+
+/** returns the zero-based tick (relative to session origin)
+   where the zero-based frame (relative to tempo section)
+   lies.
+*/
+double
+TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
+       }
+
+       return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
+}
+
+/** returns the zero-based frame (relative to session origin)
+   where the zero-based tick (relative to session)
+   falls.
+*/
+framepos_t
+TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
+       }
+
+       return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
+}
+
+/*
+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
+
+The beat at which a Tempo T occurs is:
+b(T) = (T - T0) / c
+
+The Tempo at which beat b occurs is:
+T(b) = b.c + T0
+
+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.
+
+The public methods are session-relative.
+
+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 (beats into global start) 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 - beat()) * BBT_Time::ticks_per_beat);
+}
+
+/* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
+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(), frame_rate));
+}
+
+framecnt_t
+TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+{
+       return (framecnt_t) floor ((time * 60.0 * frame_rate));
+}
+
+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 tempo in tpm */
+double
+TempoSection::tick_at_tick_tempo (double tick_tempo) const
+{
+       return (tick_tempo - ticks_per_minute()) / _c_func;
+}
+
+/* tempo in tpm at tick */
+double
+TempoSection::tick_tempo_at_tick (double tick) const
+{
+       return (tick * _c_func) + ticks_per_minute();
+}
+
+/* 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;
+}
+
+/* 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;
-       
+       new_beat = beat();
+
        double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
-       new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
-       new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
+       new_beat = ticks / BBT_Time::ticks_per_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));
+       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_start (new_start);
+       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_("C"));
-
-       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 +572,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 +583,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 (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 +608,20 @@ MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg (X_("C"));
+       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,21 +634,23 @@ 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)
 {
        _frame_rate = fr;
-       BBT_Time start;
-
-       start.bars = 1;
-       start.beats = 1;
-       start.ticks = 0;
+       BBT_Time start (1, 1, 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);
@@ -294,6 +659,7 @@ TempoMap::TempoMap (framecnt_t fr)
 
        metrics.push_back (t);
        metrics.push_back (m);
+
 }
 
 TempoMap::~TempoMap ()
@@ -307,23 +673,11 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 
        {
                Glib::Threads::RWLock::WriterLock lm (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;
-                                       }
-                               }
+               if ((removed = remove_tempo_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
-
-               if (removed && complete_operation) {
-                       recompute_map (false);
-               }
        }
 
        if (removed && complete_operation) {
@@ -331,6 +685,25 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
        }
 }
 
+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)
 {
@@ -338,23 +711,11 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 
        {
                Glib::Threads::RWLock::WriterLock lm (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;
-                                       }
-                               }
+               if ((removed = remove_meter_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
-
-               if (removed && complete_operation) {
-                       recompute_map (true);
-               }
        }
 
        if (removed && complete_operation) {
@@ -362,38 +723,49 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
        }
 }
 
+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);
                }
        }
 
-       
-
        /* Look for any existing MetricSection that is of the same type and
           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,
@@ -403,49 +775,51 @@ TempoMap::do_insert (MetricSection* section)
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
-               bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
-               bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
+               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+               TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
 
-               if (iter_is_tempo && insert_is_tempo) {
+               if (tempo && insert_tempo) {
 
                        /* 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 ((*i)->start().bars == section->start().bars &&
-                           (*i)->start().beats == section->start().beats) {
+                               if (!tempo->movable()) {
 
-                               if (!(*i)->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*>(section));
+
+                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
                                }
                                break;
-                       } 
+                       }
 
-               } else if (!iter_is_tempo && !insert_is_tempo) {
+               } else if (!tempo && !insert_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 ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
 
-                       if ((*i)->start().bars == section->start().bars) {
+                               if (!meter->movable()) {
 
-                               if (!(*i)->movable()) {
-                                       
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section
                                         */
-                                       
-                                       *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
+
+                                       *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
-                                       
+
                                }
 
                                break;
@@ -460,33 +834,138 @@ TempoMap::do_insert (MetricSection* section)
         */
 
        if (need_add) {
+               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);
 
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       if ((*i)->start() > section->start()) {
-                               break;
+                               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;
+                                       }
+                               }
+                       }
+               } else if (insert_tempo) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+
+                               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)
+/**
+* This is for a gui that needs to know the frame of a beat if a tempo section were to be moved or altered.
+* It actually alters tha ramps up to the beat parameter, so calling this commits you to replacing the section immediately.
+* It will not emit a signal or recompute the map, as you probably want to replace the tempo or do somethig else before that happens.
+* @param section - the section you want to alter
+* @param bpm - the new tempo
+* @param beat - the beat where the altered tempo will fall
+* @return returns - the position in frames where the new tempo section will lie
+*/
+framepos_t
+TempoMap::compute_replacement_tempo_section (TempoSection* section, const Tempo& bpm, const double& beat)
 {
-       TempoSection& first (first_tempo());
+       Glib::Threads::RWLock::WriterLock lm (lock);
 
-       if (ts.start() != first.start()) {
-               remove_tempo (ts, false);
-               add_tempo (tempo, where);
-       } else {
-               {
-                       Glib::Threads::RWLock::WriterLock lm (lock);
-                       /* cannot move the first tempo section */
-                       *static_cast<Tempo*>(&first) = tempo;
-                       recompute_map (false);
+       TempoSection* prev_ts = 0;
+       TempoSection* t;
+       framepos_t ret = 0;
+       MetricSectionSorter cmp;
+
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (section->beat() == t->beat()) {
+                                       continue;
+                               }
+                               if (t->beat() > beat){
+                                       prev_ts->set_c_func_from_tempo_and_beat (bpm.beats_per_minute(), beat, _frame_rate);
+                                       section->set_beat (beat);
+                                       section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
+                                       break;
+                               }
+
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
+                                       t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
+                               }
+                       }
+                       prev_ts = t;
+               }
+       }
+       /* now we do the whole thing again because audio-locked sections will have caused a re-order */
+       prev_ts = 0;
+       metrics.sort (cmp);
+
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (section->beat() == t->beat()) {
+                                       continue;
+                               }
+                               if (t->beat() > beat){
+                                       prev_ts->set_c_func_from_tempo_and_beat (bpm.beats_per_minute(), beat, _frame_rate);
+                                       ret = prev_ts->frame_at_beat (beat, _frame_rate);
+                                       section->set_frame (ret);
+                                       prev_ts = section;
+                                       break;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
+                                       t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
+                               }
+                       }
+                       prev_ts = t;
+               }
+       }
+
+       if (!ret) {
+               prev_ts->set_c_func_from_tempo_and_beat (bpm.beats_per_minute(), beat, _frame_rate);
+               section->set_beat (beat);
+               section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
+
+               ret = prev_ts->frame_at_beat (beat, _frame_rate);
+       }
+
+       return ret;
+}
+
+void
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
+{
+       {
+               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);
+                       }
                }
        }
 
@@ -494,66 +973,259 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T
 }
 
 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::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);
+                       }
+               }
+       }
+
+       PropertyChanged (PropertyChange ());
+}
 
-               /* new tempos always start on a beat */
-               where.ticks = 0;
+Metrics
+TempoMap::get_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame, const double& beat)
+{
+       Metrics imaginary (metrics);
 
-               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.
-                */
+       TempoSection* prev_ts = 0;
+       TempoSection* t;
 
-               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;
+       /*set frame and sort */
+       section->set_frame (frame);
+       MetricSectionFrameSorter fcmp;
+       imaginary.sort (fcmp);
+
+       /* recompute */
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t == section) {
+                                       /* we have already set the frame - set the beat */
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
+                                       //section->set_beat (prev_ts->beat_at_frame (frame, _frame_rate));
+                                       section->set_beat (prev_ts->beat_at_tempo (bpm.beats_per_minute(), frame, _frame_rate));
+                                       prev_ts = t;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
+                                       //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                                       t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
+                                       t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
+                               }
                        }
-                       
-                       if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                               meter = m;
+                       prev_ts = t;
+               }
+       }
+       MetricSectionSorter cmp;
+       imaginary.sort (cmp);
+       /* to do - check precision using _at_tempo() methods */
+/*
+       prev_ts = 0;
+       std::cerr << "dumping imaginary order ------" << std::endl;;
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+
+                               std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
+                               std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
+                               std::cerr << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute()) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), _frame_rate) << std::endl;
+                               std::cerr << "   ------" << std::endl;;
+
+                       }
+                       prev_ts = t;
+               }
+       }
+       std::cerr << "end dump ------";
+*/
+       return imaginary;
+}
+
+Metrics
+TempoMap::get_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat)
+{
+       /* incomplete */
+       Metrics imaginary (metrics);
+
+       MeterSection* prev_ms = 0;
+       MeterSection* m;
+       MeterSection* our_section = 0;
+       framepos_t ret = 0;
+       MetricSectionSorter cmp;
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               if (section->beat() == m->beat()) {
+                                       our_section = m;
+                                       continue;
+                               }
+                               if (beat < m->beat()){
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
+                                       our_section->set_beat (b_bbt);
+                                       our_section->set_frame (frame_at_beat_locked (beat));
+                                       break;
+                               }
+
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_beat_locked (m->beat()));
+                               } else {
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
+                                       m->set_beat (b_bbt);
+                               }
+                       }
+                       prev_ms = m;
+               }
+       }
+       /* now we do the whole thing again because audio-locked sections will have caused a re-order */
+       prev_ms = 0;
+       metrics.sort (cmp);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               if (section->beat() == m->beat()) {
+                                       continue;
+                               }
+                               if (beat < m->beat()){
+                                       ret = frame_at_beat (beat);
+                                       section->set_frame (ret);
+                                       prev_ms = section;
+                                       break;
+                               }
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_beat (m->beat()));
+                               } else {
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
+                                       m->set_beat (b_bbt);
+                               }
                        }
+                       prev_ms = m;
                }
+       }
 
-               ts->update_bar_offset_from_bbt (*meter);
+       if (!ret) {
+               pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
+               section->set_beat (b_bbt);
+               section->set_frame (frame_at_beat_locked (beat));
+       }
+       return imaginary;
+}
 
-               /* and insert it */
-               
-               do_insert (ts);
+void
+TempoMap::gui_move_tempo (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame, const double& beat_where)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               Metrics new_order = get_new_order (ts, bpm, frame, beat_where);
 
+               metrics.clear();
+               metrics = new_order;
                recompute_map (false);
        }
 
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame, const double&  beat_where)
+{
+       Glib::Threads::RWLock::WriterLock lm (lock);
+       Metrics imaginary = get_new_order (ms, mt, frame, beat_where);
+}
+
+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)
 {
-       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.start() != first.start()) {
-               remove_meter (ms, false);
-               add_meter (meter, where);
-       } else {
-               {
-                       Glib::Threads::RWLock::WriterLock lm (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 */
                        *static_cast<Meter*>(&first) = meter;
                        recompute_map (true);
@@ -563,32 +1235,34 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
        PropertyChanged (PropertyChange ());
 }
 
+
 void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
 {
        {
                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()));
-               recompute_map (true);
+void
+TempoMap::add_meter (const Meter& meter, framepos_t frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_meter_locked (meter, frame, true);
        }
 
-       
+
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
                dump (std::cerr);
@@ -598,6 +1272,45 @@ 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. */
+       do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
+
+       if (recompute) {
+               recompute_map (true);
+       }
+
+}
+
 void
 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
@@ -606,7 +1319,7 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       { 
+                       {
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
                                recompute_map (false);
@@ -687,6 +1400,8 @@ 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;
@@ -703,6 +1418,8 @@ TempoMap::first_tempo () const
 {
        const TempoSection *t = 0;
 
+       /* 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;
@@ -729,36 +1446,67 @@ TempoMap::first_tempo ()
        abort(); /*NOTREACHED*/
        return *t;
 }
-
 void
-TempoMap::require_map_to (framepos_t pos)
+TempoMap::recompute_tempos ()
 {
-       Glib::Threads::RWLock::WriterLock lm (lock);
+       TempoSection* prev_ts = 0;
 
-       if (_map.empty() || _map.back().frame < pos) {
-               extend_map (pos);
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               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(), _frame_rate));
+                                               //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
+                                               t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       } else {
+                                               prev_ts->set_c_func (0.0);
+                                               //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
+                                               t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       }
+                               } else {
+                                       if (prev_ts->type() == TempoSection::Ramp) {
+                                               prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
+                                               //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                                               t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
+                                       } else {
+                                               prev_ts->set_c_func (0.0);
+                                               //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                                               t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
+                                       }
+                               }
+                       }
+                       prev_ts = t;
+               }
        }
 }
 
 void
-TempoMap::require_map_to (const BBT_Time& bbt)
+TempoMap::recompute_meters ()
 {
-       Glib::Threads::RWLock::WriterLock lm (lock);
-
-       /* since we have no idea where BBT is if its off the map, see the last
-        * point in the map is past BBT, and if not add an arbitrary amount of
-        * time until it is.
-        */
+       MeterSection* meter = 0;
 
-       int additional_minutes = 1;
-       
-       while (1) {
-               if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
-                       break;
+       for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
+               /* Now we have the tempos mapped to position, 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;
+
+                               where.bars = 1;
+                               where.beats = 1;
+                               where.ticks = 0;
+
+                               pr.first = beat_at_frame_locked (meter->frame());
+                               pr.second = where;
+                               meter->set_beat (pr);
+                       } else {
+                               meter->set_frame (frame_at_tick_locked (meter->tick()));
+                       }
                }
-               /* add some more distance, using bigger steps each time */
-               extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
-               additional_minutes *= 2;
        }
 }
 
@@ -767,12 +1515,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
-       MeterSection* meter = 0;
-       TempoSection* tempo = 0;
-       double current_frame;
-       BBT_Time current;
-       Metrics::iterator next_metric;
-
        if (end < 0) {
 
                /* we will actually stop once we hit
@@ -780,402 +1522,279 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                */
                end = max_framepos;
 
-       } else {
-               if (!_map.empty ()) {
-                       /* never allow the map to be shortened */
-                       end = max (end, _map.back().frame);
-               }
        }
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* ms;
-
-               if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       meter = ms;
-                       break;
-               }
-       }
-
-       assert(meter);
-
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* ts;
-
-               if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       tempo = ts;
-                       break;
-               }
+       if (end == 0) {
+               /* silly call from Session::process() during startup
+                */
+               return;
        }
 
-       assert(tempo);
+       recompute_tempos();
+       recompute_meters();
+}
 
-       /* 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;
+TempoMetric
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
 
-       if (reassign_tempo_bbt) {
+       /* 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.
 
-               MeterSection* rmeter = meter;
+          now see if we can find better candidates.
+       */
 
-               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((*i)->frame() > frame) {
+                       break;
+               }
 
-                       TempoSection* ts;
-                       MeterSection* ms;
-       
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+               m.set_metric(*i);
 
-                               /* reassign the BBT time of this tempo section
-                                * based on its bar offset position.
-                                */
+               if (last) {
+                       *last = i;
+               }
+       }
 
-                               ts->update_bbt_time_from_bar_offset (*rmeter);
-
-                       } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               rmeter = ms;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
-                       }
-               }
-       }
+       return m;
+}
+/* XX meters only */
+TempoMetric
+TempoMap::metric_at (BBT_Time bbt) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
+       /* 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.
 
-       next_metric = metrics.begin();
-       ++next_metric; // skip meter (or tempo)
-       ++next_metric; // skip tempo (or meter)
+          now see if we can find better candidates.
+       */
 
-       _map.clear ();
+       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());
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
-       _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
+                       if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+                               break;
+                       }
 
-       if (end == 0) {
-               /* silly call from Session::process() during startup
-                */
-               return;
+                       m.set_metric (*i);
+               }
        }
 
-       _extend_map (tempo, meter, next_metric, current, current_frame, end);
+       return m;
 }
 
 void
-TempoMap::extend_map (framepos_t end)
+TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
 {
-       /* CALLER MUST HOLD WRITE LOCK */
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       if (_map.empty()) {
-               recompute_map (false, end);
+       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_locked (frame));
+}
 
-       BBTPointList::const_iterator i = _map.end();    
-       Metrics::iterator next_metric;
-
-       --i;
-
-       BBT_Time last_metric_start;
+double
+TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return bbt_to_beats_locked (bbt);
+}
 
-       if ((*i).tempo->frame() > (*i).meter->frame()) {
-               last_metric_start = (*i).tempo->start();
-       } else {
-               last_metric_start = (*i).meter->start();
-       }
+double
+TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
+{
+       /* CALLER HOLDS READ LOCK */
 
-       /* find the metric immediately after the tempo + meter sections for the
-        * last point in the map 
-        */
+       double accumulated_beats = 0.0;
+       double accumulated_bars = 0.0;
+       MeterSection* prev_ms = 0;
 
-       for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
-               if ((*next_metric)->start() > last_metric_start) {
-                       break;
+       for (Metrics::const_iterator 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;
                }
        }
 
-       /* we cast away const here because this is the one place where we need
-        * to actually modify the frame time of each metric section. 
-        */
-
-       _extend_map (const_cast<TempoSection*> ((*i).tempo), 
-                    const_cast<MeterSection*> ((*i).meter),
-                    next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
+       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;
 }
 
-void
-TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 
-                      Metrics::iterator next_metric,
-                      BBT_Time current, framepos_t current_frame, framepos_t end)
+Timecode::BBT_Time
+TempoMap::beats_to_bbt (double beats)
 {
-       /* CALLER MUST HOLD WRITE LOCK */
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beats_to_bbt_locked (beats);
+}
 
-       TempoSection* ts;
-       MeterSection* ms;
-       double beat_frames;
-       double current_frame_exact;
-       framepos_t bar_start_frame;
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_locked (double beats)
+{
+       /* CALLER HOLDS READ LOCK */
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
+       MeterSection* prev_ms = 0;
+       uint32_t accumulated_bars = 0;
 
-       if (current.beats == 1) {
-               bar_start_frame = current_frame;
-       } else {
-               bar_start_frame = 0;
-       }
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-       beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
-       current_frame_exact = current_frame;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-       while (current_frame < end) {
+                       if (beats < m->beat()) {
+                               /* this is the meter after the one our beat is on*/
+                               break;
+                       }
 
-               current.beats++;
-               current_frame_exact += beat_frames;
-               current_frame = llrint(current_frame_exact);
+                       if (prev_ms) {
+                               /* we need a whole number of bars. */
+                               accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
+                       }
 
-               if (current.beats > meter->divisions_per_bar()) {
-                       current.bars++;
-                       current.beats = 1;
+                       prev_ms = m;
                }
+       }
 
-               if (next_metric != metrics.end()) {
-
-                       /* no operator >= so invert operator < */
-
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
-
-                       if (!(current < (*next_metric)->start())) {
-
-               set_metrics:
-                               if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
-
-                                       tempo = ts;
-
-                                       /* 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.
-                                        */
-
-                                       if (tempo->start().ticks != 0) {
-                                               
-                                               double next_beat_frames = tempo->frames_per_beat (_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_exact -= beat_frames;
-                                               current_frame = llrint(current_frame_exact);
-
-                                               /* set tempo section location
-                                                * based on offset from last
-                                                * bar start 
-                                                */
-                                               tempo->set_frame (bar_start_frame + 
-                                                                 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
-                                               
-                                               /* advance to the location of
-                                                * the new (adjusted) beat. do
-                                                * this by figuring out the
-                                                * offset within the beat that
-                                                * would have been there
-                                                * without the tempo
-                                                * change. then stretch the
-                                                * beat accordingly.
-                                                */
-
-                                               double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
-
-                                               current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
-                                               current_frame = llrint(current_frame_exact);
-
-                                               /* 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);
-                                       }
-
-                               } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
-                                       
-                                       meter = ms;
+       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;
 
-                                       /* 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);
+       BBT_Time ret;
 
-                                       meter->set_frame (current_frame);
-                               }
-                               
-                               beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
-                               
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", 
-                                                                              beat_frames, meter->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;
-                               }
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-                       }
-               } 
+       /* 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));
-                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
-                       bar_start_frame = current_frame;
-               } else {
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
-                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
-               }
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
+       }
 
-               if (next_metric == metrics.end()) {
-                       /* no more metrics - we've timestamped them all, stop here */
-                       if (end == max_framepos) {
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
-                                                                              current.bars, current.beats, current_frame));
-                               break;
-                       }
-               }
+       if (ret.beats > prev_ms->divisions_per_bar()) {
+               ++ret.bars;
+               ret.beats = 1;
        }
+
+       return ret;
 }
 
-TempoMetric
-TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+double
+TempoMap::tick_at_frame_locked (framecnt_t frame) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       TempoMetric m (first_meter(), first_tempo());
-
-       /* 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.
+       /* HOLD (at least) THE READER LOCK */
 
-          now see if we can find better candidates.
-       */
+       TempoSection* prev_ts = 0;
+       double accumulated_ticks = 0.0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if ((prev_ts) && frame < t->frame()) {
+                               /*the previous ts is the one containing the frame */
+                               return prev_ts->tick_at_frame (frame, _frame_rate);
+                       }
 
-               if ((*i)->frame() > frame) {
-                       break;
+                       accumulated_ticks = t->tick();
+                       prev_ts = t;
                }
+       }
 
-               m.set_metric(*i);
+       /* treated as constant for this ts */
+       double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+
+       return ticks_in_section + accumulated_ticks;
 
-               if (last) {
-                       *last = i;
-               }
-       }
-       
-       return m;
 }
 
-TempoMetric
-TempoMap::metric_at (BBT_Time bbt) const
+framecnt_t
+TempoMap::frame_at_tick_locked (double tick) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       TempoMetric m (first_meter(), first_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.
-
-          now see if we can find better candidates.
-       */
+       const TempoSection* prev_ts = 0;
+       double accumulated_ticks = 0.0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts && tick < t->tick()) {
+                               /* prev_ts is the one affecting us. */
+                               return prev_ts->frame_at_tick (tick, _frame_rate);
+                       }
 
-               BBT_Time section_start ((*i)->start());
-
-               if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
-                       break;
+                       accumulated_ticks = t->tick();
+                       prev_ts = t;
                }
-
-               m.set_metric (*i);
        }
+       /* must be treated as constant, irrespective of _type */
+       double const ticks_in_section = tick - accumulated_ticks;
+       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
 {
-       require_map_to (frame);
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       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;
-       }
-
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
+       return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
 }
 
-void
-TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
+double
+TempoMap::beat_at_frame_locked (framecnt_t frame) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
-
-       if (!lm.locked()) {
-               throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
-       }
-       
-       if (_map.empty() || _map.back().frame < frame) {
-               throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
-       }
-
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
+       return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
 }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
+framecnt_t
+TempoMap::frame_at_beat (double beat) const
 {
-       /* CALLER MUST HOLD READ LOCK */
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
+}
 
-       bbt.bars = (*i).bar;
-       bbt.beats = (*i).beat;
+framecnt_t
+TempoMap::frame_at_beat_locked (double beat) const
+{
 
-       if ((*i).frame == frame) {
-               bbt.ticks = 0;
-       } else {
-               bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
-                                   BBT_Time::ticks_per_beat);
-       }
+       return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
 }
 
 framepos_t
@@ -1185,74 +1804,58 @@ TempoMap::frame_time (const BBT_Time& bbt)
                warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
                return 0;
        }
-       
+
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       require_map_to (bbt);
+       return frame_time_locked (bbt);;
+}
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+framepos_t
+TempoMap::frame_time_locked (const BBT_Time& bbt)
+{
+       /* HOLD THE READER LOCK */
 
-       BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
-       BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
+       framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
 
-       if (bbt.ticks != 0) {
-               return ((*e).frame - (*s).frame) + 
-                       llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
-       } else {
-               return ((*e).frame - (*s).frame);
-       }
+       return ret;
 }
 
+
 framecnt_t
 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
-       BBT_Time when;
-       bbt_time (pos, when);
-       
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_duration_at_unlocked (when, bbt, dir);
-}
 
-framecnt_t
-TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/) 
-{
-       if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
-               return 0;
-       }
+       Metrics::const_iterator i;
+       TempoSection* first = 0;
+       TempoSection* second = 0;
 
-       /* round back to the previous precise beat */
-       BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
-       BBTPointList::const_iterator start (wi);
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       assert (wi != _map.end());
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-       uint32_t bars = 0;
-       uint32_t beats = 0;
+                       if ((*i)->frame() > pos) {
+                               second = t;
+                               break;
+                       }
 
-       while (wi != _map.end() && bars < bbt.bars) {
-               ++wi;
-               if ((*wi).is_bar()) {
-                       ++bars;
+                       first = t;
                }
        }
-       assert (wi != _map.end());
+       if (first && second) {
+               double const tick_at_time = first->tick_at_frame (pos, _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);
 
-       while (wi != _map.end() && beats < bbt.beats) {
-               ++wi;
-               ++beats;
+               return time_at_bbt - pos;
        }
-       assert (wi != _map.end());
+       double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
 
-       /* add any additional frames related to ticks in the added value */
-
-       if (bbt.ticks != 0) {
-               return ((*wi).frame - (*start).frame) + 
-                       (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
-       } else {
-               return ((*wi).frame - (*start).frame);
-       }
+       return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
 }
 
 framepos_t
@@ -1270,52 +1873,40 @@ TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
 framepos_t
 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
-       require_map_to (fr);
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       BBTPointList::const_iterator i = bbt_before_or_at (fr);
-       BBT_Time the_beat;
-       uint32_t ticks_one_subdivisions_worth;
 
-       bbt_time (fr, the_beat, i);
+       uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (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;
 
-       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));
-
-       ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
+       ticks -= beats * BBT_Time::ticks_per_beat;
 
        if (dir > 0) {
-
                /* round to next (or same iff dir == RoundUpMaybe) */
 
-               uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
+               uint32_t mod = ticks % ticks_one_subdivisions_worth;
 
                if (mod == 0 && dir == RoundUpMaybe) {
                        /* right on the subdivision, which is fine, so do nothing */
 
                } 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_beat) {
-                       assert (i != _map.end());
-                       ++i;
-                       assert (i != _map.end());
-                       the_beat.ticks -= BBT_Time::ticks_per_beat;
-               } 
-
-
+               if (ticks >= BBT_Time::ticks_per_beat) {
+                       ticks -= BBT_Time::ticks_per_beat;
+               }
        } else if (dir < 0) {
 
                /* round to previous (or same iff dir == RoundDownMaybe) */
 
-               uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
+               uint32_t difference = ticks % ticks_one_subdivisions_worth;
 
                if (difference == 0 && dir == RoundDownAlways) {
                        /* right on the subdivision, but force-rounding down,
@@ -1323,243 +1914,228 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        difference = ticks_one_subdivisions_worth;
                }
 
-               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_beat - 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_beat) {
-                               assert (i != _map.end());
-                               ++i;
-                               assert (i != _map.end());
-                               the_beat.ticks -= BBT_Time::ticks_per_beat;
-                               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_beat - 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_beat) * 
-               (*i).tempo->frames_per_beat (_frame_rate);
+       return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
 }
 
 framepos_t
 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
-       require_map_to (frame);
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       BBTPointList::const_iterator fi;
 
-       if (dir > 0) {
-               fi = bbt_after_or_at (frame);
-       } else {
-               fi = bbt_before_or_at (frame);
-       }
+       double const beat_at_framepos = beat_at_frame_locked (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 %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
-                                                    (type == Bar ? "bar" : "beat")));
-               
        switch (type) {
        case Bar:
                if (dir < 0) {
                        /* find bar previous to 'frame' */
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
 
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundDownMaybe) {
-                                       return frame;
-                               }
-                               --fi;
+               } 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 prev_ft;
                        }
+               }
 
-                       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;
+               break;
 
+       case Beat:
+               if (dir < 0) {
+                       return frame_at_beat_locked (floor (beat_at_framepos));
                } else if (dir > 0) {
+                       return frame_at_beat_locked (ceil (beat_at_framepos));
+               } else {
+                       return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
+               }
+               break;
+       }
 
-                       /* find bar following 'frame' */
+       return 0;
+}
 
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundUpMaybe) {
-                                       return frame;
-                               }
-                               ++fi;
-                       }
+void
+TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
+                   framepos_t lower, framepos_t upper)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (upper));
+       uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (lower));
 
-                       while (!(*fi).is_bar()) {
-                               fi++;
-                               if (fi == _map.end()) {
-                                       --fi;
-                                       break;
-                               }
-                       }
+       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);
 
-                       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;
+               points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
+               ++cnt;
+       }
+}
 
-               } else {
-                       
-                       /* true rounding: find nearest bar */
+const TempoSection&
+TempoMap::tempo_section_at (framepos_t frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
+       Metrics::const_iterator i;
+       TempoSection* prev = 0;
 
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-                       while ((*prev).beat != 1) {
-                               if (prev == _map.begin()) {
-                                       break;
-                               }
-                               prev--;
-                       }
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                       while ((next != _map.end()) && (*next).beat != 1) {
-                               next++;
+                       if ((*i)->frame() > frame) {
+                               break;
                        }
 
-                       if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
-                       } else {
-                               return (*next).frame;
-                       }
-                       
+                       prev = t;
                }
+       }
 
-               break;
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
 
-       case Beat:
-               if (dir < 0) {
+       return *prev;
+}
 
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
+/* 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);
 
-                       if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) {
-                               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;
-               } else if (dir > 0) {
-                       if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) {
-                               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;
-               } else {
-                       /* find beat nearest to frame */
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
+       const TempoSection* ts_at = &tempo_section_at (frame);
+       const TempoSection* ts_after = 0;
+       Metrics::const_iterator i;
 
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       /* fi is already the beat before_or_at frame, and
-                          we've just established that its not at frame, so its
-                          the beat before frame.
-                       */
-                       ++next;
-                       
-                       if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
-                       } else {
-                               return (*next).frame;
+       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;
                        }
                }
-               break;
        }
 
-       abort(); /* NOTREACHED */
-       return 0;
+       if (ts_after) {
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
+       }
+       /* must be treated as constant tempo */
+       return ts_at->frames_per_beat (_frame_rate);
 }
 
-void
-TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, 
-                   TempoMap::BBTPointList::const_iterator& end, 
-                   framepos_t lower, framepos_t upper) 
+const Tempo
+TempoMap::tempo_at (framepos_t frame) const
 {
-       { 
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               if (_map.empty() || (_map.back().frame < upper)) {
-                       recompute_map (false, upper);
+       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 */
+                               double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
+                               Tempo const ret_tempo (ret, m.tempo().note_type ());
+                               return ret_tempo;
+                       }
+                       prev_ts = t;
                }
        }
 
-       begin = lower_bound (_map.begin(), _map.end(), lower);
-       end = upper_bound (_map.begin(), _map.end(), upper);
+       return m.tempo();
 }
 
-const TempoSection&
-TempoMap::tempo_section_at (framepos_t frame) const
+const MeterSection&
+TempoMap::meter_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+
        Metrics::const_iterator i;
-       TempoSection* prev = 0;
+       MeterSection* prev = 0;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
+               MeterSection* t;
 
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+               if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
 
                        if ((*i)->frame() > frame) {
                                break;
@@ -1577,14 +2153,6 @@ TempoMap::tempo_section_at (framepos_t frame) const
        return *prev;
 }
 
-const Tempo&
-TempoMap::tempo_at (framepos_t frame) const
-{
-       TempoMetric m (metric_at (frame));
-       return m.tempo();
-}
-
-
 const Meter&
 TempoMap::meter_at (framepos_t frame) const
 {
@@ -1621,7 +2189,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                metrics.clear();
 
                nlist = node.children();
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
@@ -1633,8 +2201,8 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                        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);
+                                               }
                                        }
                                }
 
@@ -1664,7 +2232,24 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        MetricSectionSorter 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.
                */
@@ -1672,14 +2257,20 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                Metrics::iterator prev = metrics.end();
                for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                        if (prev != metrics.end()) {
-                               if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
+                               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 (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
+                               } 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;
                                        }
                                }
@@ -1705,10 +2296,10 @@ TempoMap::dump (std::ostream& o) const
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       o << "Tempo @ " << *i << " (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;
                }
        }
@@ -1765,295 +2356,187 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                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);
-                       
+                       //TempoMetric metric (*meter, *tempo);
+                       MeterSection* ms = const_cast<MeterSection*>(meter);
+                       TempoSection* ts = const_cast<TempoSection*>(tempo);
                        if (prev) {
-                               metric.set_start (prev->start());
-                               metric.set_frame (prev->frame());
+                               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
                        }
-                       
-                       BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
-                       bbt_time ((*i)->frame(), bbt, bi);
-                       
-                       // 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;
-                               }
-                       }
-                       
+
                        // cerr << bbt << endl;
-                       
-                       (*i)->set_start (bbt);
-                       
+
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               t->set_beat (beat_at_frame_locked (m->frame()));
                                tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+                               // 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_locked (m->frame()), bbt);
+                               m->set_beat (start);
                                meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+                               // 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);
        }
 
 
        PropertyChanged (PropertyChange ());
 }
-
-/** 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::Beats beats) const
+bool
+TempoMap::remove_time (framepos_t where, framecnt_t amount)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_iterator next_tempo;
-       const TempoSection* tempo = 0;
-
-       /* Find the starting tempo metric */
-
-       for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
-
-               const TempoSection* t;
+       bool moved = false;
 
-               if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
+       std::list<MetricSection*> metric_kill_list;
 
-                       /* This is a bit of a hack, but pos could be -ve, and if it is,
-                          we consider the initial metric changes (at time 0) to actually
-                          be in effect at pos.
-                       */
-
-                       framepos_t f = (*next_tempo)->frame ();
-
-                       if (pos < 0 && f == 0) {
-                               f = pos;
+       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;
                        }
-                       
-                       if (f > pos) {
-                               break;
+                       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;
                        }
-                       
-                       tempo = t;
                }
-       }
-
-       /* We now have:
-
-          tempo       -> the Tempo for "pos"
-          next_tempo  -> first tempo after "pos", possibly metrics.end()
-       */
-       assert(tempo);
-
-       DEBUG_TRACE (DEBUG::TempoMath,
-                    string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
-                                    pos, beats, *((const Tempo*)tempo), tempo->frame()));
-
-       while (!!beats) {
-
-               /* Distance to the end of this section in frames */
-               framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
-
-               /* Distance to the end in beats */
-               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
-                       distance_frames, tempo->frames_per_beat (_frame_rate));
-
-               /* Amount to subtract this time */
-               Evoral::Beats const delta = min (distance_beats, beats);
-
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
-                                                              (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
-                                                              distance_frames, distance_beats));
-
-               /* Update */
-               beats -= delta;
-               pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate));
 
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
-
-               /* step forwards to next tempo section */
-
-               if (next_tempo != metrics.end()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*next_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                                      *((const Tempo*)tempo), tempo->frame(),
-                                                                      tempo->frames_per_beat (_frame_rate)));
+               //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;
+               }
 
-                       while (next_tempo != metrics.end ()) {
+               //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;
+               }
 
-                               ++next_tempo;
-                               
-                               if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
-                                       break;
-                               }
-                       }
+               if (moved) {
+                       recompute_map (true);
                }
        }
+       PropertyChanged (PropertyChange ());
+       return moved;
+}
 
-       return pos;
+/** 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::Beats beats) const
+{
+       return frame_at_beat (beat_at_frame (pos) + beats.to_double());
 }
 
 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
 framepos_t
 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_reverse_iterator prev_tempo;
-       const TempoSection* tempo = 0;
-
-       /* Find the starting tempo metric */
-
-       for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
-
-               const TempoSection* t;
-
-               if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
-
-                       /* This is a bit of a hack, but pos could be -ve, and if it is,
-                          we consider the initial metric changes (at time 0) to actually
-                          be in effect at pos.
-                       */
-
-                       framepos_t f = (*prev_tempo)->frame ();
-
-                       if (pos < 0 && f == 0) {
-                               f = pos;
-                       }
-
-                       /* this is slightly more complex than the forward case
-                          because we reach the tempo in effect at pos after
-                          passing through pos (rather before, as in the
-                          forward case). having done that, we then need to
-                          keep going to get the previous tempo (or
-                          metrics.rend())
-                       */
-                       
-                       if (f <= pos) {
-                               if (tempo == 0) {
-                                       /* first tempo with position at or
-                                          before pos
-                                       */
-                                       tempo = t;
-                               } else if (f < pos) {
-                                       /* some other tempo section that
-                                          is even earlier than 'tempo'
-                                       */
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       assert(tempo);
-       DEBUG_TRACE (DEBUG::TempoMath,
-                    string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
-                                    pos, beats, *((const Tempo*)tempo), tempo->frame(),
-                                    prev_tempo == metrics.rend()));
-
-       /* We now have:
-
-          tempo       -> the Tempo for "pos"
-          prev_tempo  -> the first metric before "pos", possibly metrics.rend()
-       */
-
-       while (!!beats) {
-               
-               /* Distance to the start of this section in frames */
-               framecnt_t distance_frames = (pos - tempo->frame());
-
-               /* Distance to the start in beats */
-               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
-                       distance_frames, tempo->frames_per_beat (_frame_rate));
-
-               /* Amount to subtract this time */
-               Evoral::Beats const sub = min (distance_beats, beats);
-
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
-                                                              tempo->frame(), distance_frames, distance_beats));
-               /* Update */
-
-               beats -= sub;
-               pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate);
-
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
-                                                              (prev_tempo == metrics.rend())));
-
-               /* step backwards to prior TempoSection */
-
-               if (prev_tempo != metrics.rend()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath,
-                                    string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                    *((const Tempo*)tempo), tempo->frame(),
-                                                    tempo->frames_per_beat (_frame_rate)));
-
-                       while (prev_tempo != metrics.rend ()) {
-
-                               ++prev_tempo;
-
-                               if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
-                                       break;
-                               }
-                       }
-               } else {
-                       pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
-                       beats = Evoral::Beats();
-               }
-       }
-
-       return pos;
+       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_plus_bbt (framepos_t pos, BBT_Time op) const
 {
+       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);
@@ -2079,10 +2562,20 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                }
        }
 
+       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;
+               }
+       }
+
        /* 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()
        */
 
@@ -2090,9 +2583,9 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
           checking for a new metric on every beat.
        */
 
-       frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
        uint64_t bars = 0;
+       /* fpb is used for constant tempo */
+       frames_per_beat = tempo->frames_per_beat (_frame_rate);
 
        while (op.bars) {
 
@@ -2111,8 +2604,17 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                 * traversed before we change the
                                 * frames_per_beat value.
                                 */
-                               
-                               pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
+
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       next_tempo = t;
+                               }
+
+                               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()));
+                               }
+
                                bars = 0;
 
                                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
@@ -2122,13 +2624,16 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                }
                                ++i;
                                frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
                        }
                }
 
        }
 
-       pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
+       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()));
+       }
 
        uint64_t beats = 0;
 
@@ -2152,7 +2657,16 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                 * frames_per_beat value.
                                 */
 
-                               pos += llrint (beats * frames_per_beat);
+                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+                                       next_tempo = t;
+                               }
+
+                               if (next_tempo) {
+                                       pos += tempo->frame_at_beat (beats, _frame_rate);
+                               } else {
+                                       pos += llrint (beats * frames_per_beat);
+                               }
+
                                beats = 0;
 
                                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
@@ -2166,19 +2680,18 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                }
        }
 
-       pos += llrint (beats * frames_per_beat);
+       if (next_tempo) {
+               pos += tempo->frame_at_beat (beats, _frame_rate);
+       } else {
+               pos += llrint (beats * frames_per_beat);
+       }
 
        if (op.ticks) {
-               if (op.ticks >= BBT_Time::ticks_per_beat) {
-                       pos += llrint (frames_per_beat + /* extra beat */
-                                      (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / 
-                                                          (double) BBT_Time::ticks_per_beat)));
-               } else {
-                       pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
-               }
+               pos += tempo->frame_at_tick (op.ticks, _frame_rate);
        }
 
        return pos;
+
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
@@ -2187,126 +2700,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_iterator next_tempo;
-       const TempoSection* tempo = 0;
-       framepos_t effective_pos = max (pos, (framepos_t) 0);
-
-       /* Find the relevant initial tempo metric  */
-
-       for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
-
-               const TempoSection* t;
-
-               if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
-
-                       if ((*next_tempo)->frame() > effective_pos) {
-                               break;
-                       }
-
-                       tempo = t;
-               }
-       }
-
-       /* We now have:
-
-          tempo -> the Tempo for "pos"
-          next_tempo -> the next tempo after "pos", possibly metrics.end()
-       */
-       assert (tempo);
-
-       DEBUG_TRACE (DEBUG::TempoMath,
-                    string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
-                                    pos, distance, *((const Tempo*)tempo), tempo->frame()));
-       
-       Evoral::Beats beats = Evoral::Beats();
-
-       while (distance) {
-
-               /* End of this section */
-               framepos_t end;
-               /* Distance to `end' in frames */
-               framepos_t distance_to_end;
-
-               if (next_tempo == metrics.end ()) {
-                       /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
-                       end = max_framepos;
-                       distance_to_end = max_framepos;
-               } else {
-                       end = (*next_tempo)->frame ();
-                       distance_to_end = end - pos;
-               }
-
-               /* Amount to subtract this time in frames */
-               framecnt_t const sub = min (distance, distance_to_end);
-
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
-                                                              distance_to_end, sub));
-
-               /* Update */
-               pos += sub;
-               distance -= sub;
-               assert (tempo);
-               beats += Evoral::Beats::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate));
-
-               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
-                                                              pos, beats, distance));
-
-               /* Move on if there's anything to move to */
-
-               if (next_tempo != metrics.end()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*next_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath,
-                                    string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                    *((const Tempo*)tempo), tempo->frame(),
-                                                    tempo->frames_per_beat (_frame_rate)));
-
-                       while (next_tempo != metrics.end ()) {
-
-                               ++next_tempo;
-                               
-                               if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
-                                       break;
-                               }
-                       }
-
-                       if (next_tempo == metrics.end()) {
-                               DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
-                       } else {
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
-                                                                              **next_tempo, (*next_tempo)->frame()));
-                       }
-
-               }
-               assert (tempo);
-       }
-
-       return beats;
-}
-
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_before_or_at (framepos_t pos)
-{
-       /* CALLER MUST HOLD READ LOCK */
-
-       BBTPointList::const_iterator i;
-
-       if (pos < 0) {
-               /* not really correct, but we should catch pos < 0 at a higher
-                  level 
-               */
-               return _map.begin();
-       }
-
-       i = lower_bound (_map.begin(), _map.end(), pos);
-       assert (i != _map.end());
-       if ((*i).frame > pos) {
-               assert (i != _map.begin());
-               --i;
-       }
-       return i;
+       return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
 }
 
 struct bbtcmp {
@@ -2315,54 +2709,20 @@ struct bbtcmp {
     }
 };
 
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_before_or_at (const BBT_Time& bbt)
-{
-       BBTPointList::const_iterator i;
-       bbtcmp cmp;
-
-       i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
-       assert (i != _map.end());
-       if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
-               assert (i != _map.begin());
-               --i;
-       }
-       return i;
-}
-
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_after_or_at (framepos_t pos) 
-{
-       /* CALLER MUST HOLD READ LOCK */
-
-       BBTPointList::const_iterator i;
-
-       if (_map.back().frame == pos) {
-               i = _map.end();
-               assert (i != _map.begin());
-               --i;
-               return i;
-       }
-
-       i = upper_bound (_map.begin(), _map.end(), pos);
-       assert (i != _map.end());
-       return i;
-}
-
-std::ostream& 
+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;
@@ -2370,7 +2730,7 @@ operator<< (std::ostream& o, const MetricSection& section) {
        if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
                o << *((const Tempo*) ts);
        } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
-               o << *((const Meter*) ms);
+               //o << *((const Meter*) ms);
        }
 
        return o;