Tempo ramps - small bug fixes/cleanup.
[ardour.git] / libs / ardour / tempo.cc
index 1d5c3f898c923fa82c5211af1a31629e4b399299..ea18be39c024800670ab6491b2a67cfc181b2f4b 100644 (file)
 #include <unistd.h>
 
 #include <glibmm/threads.h>
+
+#include "pbd/enumwriter.h"
 #include "pbd/xml++.h"
 #include "evoral/Beats.hpp"
+
 #include "ardour/debug.h"
 #include "ardour/lmath.h"
 #include "ardour/tempo.h"
@@ -70,26 +73,43 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
 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())
 {
-       XMLProperty const * prop;
-       BBT_Time start;
-       LocaleGuard lg ();
-
-       if ((prop = node.property ("start")) == 0) {
-               error << _("TempoSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
+       const XMLProperty *prop;
+       LocaleGuard lg;
+       BBT_Time bbt;
+       double pulse;
+       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;
+                       pulse = -1.0;
+                       set_pulse (pulse);
+               }
+       } 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 ("pulse")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
+                       error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
+               } else {
+                       set_pulse (pulse);
+               }
+       }
+       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 +146,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 +165,12 @@ TempoSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg ();
+       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", pulse());
+       root->add_property ("pulse", 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 +179,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,35 +189,276 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
+       _bar_offset = (pulse() * 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, pulse(), m.divisions_per_bar()));
+}
+
+void
+TempoSection::set_type (Type type)
+{
+       _type = type;
+}
+
+/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
+*/
+double
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+{
+
+       if (_type == Constant) {
+               return pulses_per_minute();
+       }
+
+       return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
+}
+
+/** returns the zero-based frame (relative to session)
+   where the tempo in whole pulses per minute 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 (const double& ppm, const double& b, const framecnt_t& frame_rate) const
+{
+       if (_type == Constant) {
+               return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
+       }
+
+       return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
+}
+/** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
+*/
+double
+TempoSection::tempo_at_pulse (const double& p) const
+{
+
+       if (_type == Constant) {
+               return pulses_per_minute();
+       }
+       double const ppm = pulse_tempo_at_pulse (p - pulse());
+       return ppm;
+}
+
+/** returns the zero-based beat (relative to session)
+   where the tempo in whole pulses per minute 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::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
+{
+       if (_type == Constant) {
+               double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+               return  beats;
+       }
+
+       return pulse_at_pulse_tempo (ppm) + pulse();
+}
+
+/** returns the zero-based pulse (relative to session origin)
+   where the zero-based frame (relative to session)
+   lies.
+*/
+double
+TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+{
+       if (_type == Constant) {
+               return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+       }
+
+       return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
+}
+
+/** returns the zero-based frame (relative to session start frame)
+   where the zero-based pulse (relative to session start)
+   falls.
+*/
+
+framepos_t
+TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
+{
+       if (_type == Constant) {
+               return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+       }
+
+       return minute_to_frame (time_at_pulse (p - pulse()), 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
+
+*/
+
+/*
+  compute this ramp's function constant using the end tempo (in whole pulses per minute)
+  and duration (pulses into global start) of some later tempo section.
+*/
+double
+TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
+{
+       double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
+       return pulses_per_minute() *  (exp (log_tempo_ratio) - 1) / (end_pulse - pulse());
+}
+
+/* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
+double
+TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
+{
+       return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
+}
+
+framecnt_t
+TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
+{
+       return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
+}
+
+double
+TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
+{
+       return (frame / (double) frame_rate) / 60.0;
+}
+
+/* position function */
+double
+TempoSection::a_func (double end_bpm, double c_func) const
+{
+       return log (end_bpm / pulses_per_minute()) /  c_func;
+}
+
+/*function constant*/
+double
+TempoSection::c_func (double end_bpm, double end_time) const
+{
+       return log (end_bpm / pulses_per_minute()) /  end_time;
+}
+
+/* tempo in ppm at time in minutes */
+double
+TempoSection::pulse_tempo_at_time (const double& time) const
+{
+       return exp (_c_func * time) * pulses_per_minute();
+}
+
+/* time in minutes at tempo in ppm */
+double
+TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
+{
+       return log (pulse_tempo / pulses_per_minute()) / _c_func;
+}
+
+/* tick at tempo in ppm */
+double
+TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
+{
+       return (pulse_tempo - pulses_per_minute()) / _c_func;
+}
+
+/* tempo in ppm at tick */
+double
+TempoSection::pulse_tempo_at_pulse (const double& pulse) const
+{
+       return (pulse * _c_func) + pulses_per_minute();
+}
+
+/* pulse at time in minutes */
+double
+TempoSection::pulse_at_time (const double& time) const
+{
+       return ((exp (_c_func * time)) - 1) * (pulses_per_minute() / _c_func);
+}
+
+/* time in minutes at pulse */
+double
+TempoSection::time_at_pulse (const double& pulse) const
+{
+       return log (((_c_func * pulse) / pulses_per_minute()) + 1) / _c_func;
 }
 
+
 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 = pulse();
 
        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); */
-
-       /* remember the 1-based counting properties of beats */
-       new_start.beats += 1;
+       new_beat = ticks / BBT_Time::ticks_per_beat;
 
        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));
+                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
 
-       set_start (new_start);
+       set_pulse (new_beat);
 }
 
 /***********************************************************************/
@@ -192,26 +466,59 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 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())
 {
        XMLProperty const * prop;
        BBT_Time start;
-       LocaleGuard lg ();
-
-       if ((prop = node.property ("start")) == 0) {
+       LocaleGuard lg;
+       const XMLProperty *prop;
+       BBT_Time bbt;
+       double pulse = 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*/
+                       pulse = -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 ("pulse")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
+                       error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
+               }
        }
 
-       set_start (start);
+       start.first = pulse;
+
+       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();
+       }
+
+       start.second = bbt;
+       set_pulse (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 */
 
@@ -221,9 +528,8 @@ MeterSection::MeterSection (const XMLNode& node)
                        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 +537,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 +562,20 @@ MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg ();
+       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", pulse());
+       root->add_property ("pulse", 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,30 +588,32 @@ MeterSection::get_state() const
 
 struct MetricSectionSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->start() < b->start();
+           return a->pulse() < b->pulse();
+    }
+};
+
+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);
 
-       // these leak memory, well Metrics does
-       TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
-       MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
+       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
+       MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        t->set_movable (false);
        m->set_movable (false);
 
        /* note: frame time is correct (zero) for both of these */
 
-       metrics.push_back (t);
-       metrics.push_back (m);
+       _metrics.push_back (t);
+       _metrics.push_back (m);
+
 }
 
 TempoMap::~TempoMap ()
@@ -310,7 +629,7 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
                Glib::Threads::RWLock::WriterLock lm (lock);
                if ((removed = remove_tempo_locked (tempo))) {
                        if (complete_operation) {
-                               recompute_map (true);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -325,11 +644,11 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
 {
        Metrics::iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++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);
+                                       _metrics.erase (i);
                                        return true;
                                }
                        }
@@ -348,7 +667,7 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
                Glib::Threads::RWLock::WriterLock lm (lock);
                if ((removed = remove_meter_locked (tempo))) {
                        if (complete_operation) {
-                               recompute_map (true);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -363,11 +682,11 @@ TempoMap::remove_meter_locked (const MeterSection& tempo)
 {
        Metrics::iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++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);
+                                       _metrics.erase (i);
                                        return true;
                                }
                        }
@@ -381,34 +700,25 @@ 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.
         */
+       MeterSection* m = 0;
+       if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
+               //assert (m->bbt().ticks == 0);
 
-       if (dynamic_cast<MeterSection*>(section)) {
-
-               /* 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;
+               if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
+                       pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
+                       corrected.second.beats = 1;
+                       corrected.second.ticks = 0;
+                       corrected.first = bbt_to_beats_locked (_metrics, 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_pulse (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,
@@ -416,50 +726,54 @@ TempoMap::do_insert (MetricSection* section)
           guaranteed that there is only one such match.
        */
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       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);
+               MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+               MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
 
-               if (iter_is_tempo && insert_is_tempo) {
+               if (tempo && insert_tempo) {
 
                        /* Tempo sections */
+                       bool const ipm = insert_tempo->position_lock_style() == MusicTime;
+                       if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
 
-                       if ((*i)->start().bars == section->start().bars &&
-                           (*i)->start().beats == section->start().beats) {
-
-                               if (!(*i)->movable()) {
+                               if (!tempo->movable()) {
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section).
                                         */
 
-                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
+                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        need_add = false;
                                } else {
-                                       metrics.erase (i);
+                                       _metrics.erase (i);
                                }
                                break;
                        }
 
-               } else if (!iter_is_tempo && !insert_is_tempo) {
+               } else if (meter && insert_meter) {
 
                        /* Meter Sections */
 
-                       if ((*i)->start().bars == section->start().bars) {
+                       bool const ipm = insert_meter->position_lock_style() == MusicTime;
+
+                       if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
 
-                               if (!(*i)->movable()) {
+                               if (!meter->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));
+                                       (*i)->set_position_lock_style (insert_meter->position_lock_style());
                                        need_add = false;
                                } else {
-                                       metrics.erase (i);
+                                       _metrics.erase (i);
 
                                }
 
@@ -475,34 +789,53 @@ 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;
-
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       if ((*i)->start() > section->start()) {
-                               break;
+               if (insert_meter) {
+                       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+                               MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+
+                               if (meter) {
+                                       bool const ipm = insert_meter->position_lock_style() == MusicTime;
+                                       if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && 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) {
+                                       bool const ipm = insert_tempo->position_lock_style() == MusicTime;
+                                       if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
+                                               break;
+                                       }
+                               }
                        }
                }
 
-               metrics.insert (i, section);
+               _metrics.insert (i, section);
+               //dump (_metrics, std::cerr);
        }
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
-
-               if (ts.start() != first.start()) {
+               if (ts.pulse() != first.pulse()) {
                        remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, where, true);
+                       add_tempo_locked (tempo, pulse, true, type);
                } else {
+                       first.set_type (type);
                        {
                                /* cannot move the first tempo section */
                                *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -511,59 +844,70 @@ 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);
-               add_tempo_locked (tempo, where, true);
+               TempoSection& first (first_tempo());
+               if (ts.frame() != first.frame()) {
+                       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 (_metrics);
+                       }
+               }
        }
-
-
        PropertyChanged (PropertyChange ());
 }
 
 void
-TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute)
+TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
 {
-       /* new tempos always start on a beat */
-       where.ticks = 0;
-
-       TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
-
-       /* find the meter to use to set the bar offset of this
-        * tempo section.
-        */
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_tempo_locked (tempo, pulse, true, type);
+       }
 
-       const Meter* meter = &first_meter();
+       PropertyChanged (PropertyChange ());
+}
 
-       /* 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.
+void
+TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_tempo_locked (tempo, frame, true, type);
+       }
 
-          now see if we can find better candidates.
-       */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       PropertyChanged (PropertyChange ());
+}
 
-               const MeterSection* m;
+void
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
+{
+       TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
 
-               if (where < (*i)->start()) {
-                       break;
-               }
+       do_insert (ts);
 
-               if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       meter = m;
-               }
+       if (recompute) {
+               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
        }
+}
 
-       ts->update_bar_offset_from_bbt (*meter);
-
-       /* and insert it */
+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);
+               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
        }
 }
 
@@ -573,32 +917,56 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                MeterSection& first (first_meter());
+               const PositionLockStyle pl = ms.position_lock_style();
+               if (ms.pulse() != first.pulse()) {
+                       remove_meter_locked (ms);
+                       add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
+               } else {
+                       /* cannot move the first meter section */
+                       *static_cast<Meter*>(&first) = meter;
+                       first.set_position_lock_style (pl);
+                       recompute_map (_metrics);
+               }
+       }
+
+       PropertyChanged (PropertyChange ());
+}
 
-               if (ms.start() != first.start()) {
+void
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               MeterSection& first (first_meter());
+               const PositionLockStyle pl = ms.position_lock_style();
+               if (ms.pulse() != first.pulse()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, where, true);
+                       add_meter_locked (meter, frame, true);
                } else {
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
-                       recompute_map (true);
+                       first.set_position_lock_style (pl);
+                       first.set_frame (frame);
+                       recompute_map (_metrics);
                }
        }
 
        PropertyChanged (PropertyChange ());
 }
 
+
 void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, where, true);
+               add_meter_locked (meter, beat, where, true);
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (std::cerr);
+               dump (_metrics, std::cerr);
        }
 #endif
 
@@ -606,29 +974,238 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
 }
 
 void
-TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
+TempoMap::add_meter (const Meter& meter, const framepos_t& frame)
 {
-       /* 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++;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_meter_locked (meter, frame, true);
        }
 
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (_metrics, std::cerr);
+       }
+#endif
+
+       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;
+       double pulse = pulse_at_beat_locked (_metrics, beat);
+
+       MeterSection* new_meter = new MeterSection (pulse, where, meter.divisions_per_bar(), meter.note_divisor());
+       do_insert (new_meter);
+
+       if (recompute) {
+               solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
+       }
+
+}
+
+void
+TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
+{
+
+       MeterSection* new_meter = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
 
-       do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
+       do_insert (new_meter);
 
        if (recompute) {
-               recompute_map (true);
+               solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
+       }
+
+}
+
+/**
+* This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
+* taking any possible reordering as a consequence of this into account.
+* @param section - the section to be altered
+* @param bpm - the new Tempo
+* @param bbt - the bbt where the altered tempo will fall
+* @return returns - the position in frames where the new tempo section will lie.
+*/
+framepos_t
+TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics future_map;
+       framepos_t ret = 0;
+       TempoSection* new_section = copy_metrics_and_point (future_map, section);
+
+       double const beat = bbt_to_beats_locked (future_map, bbt);
+       if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
+               ret = new_section->frame();
+       } else {
+               ret = frame_at_beat_locked (_metrics, beat);
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       return ret;
+}
+
+double
+TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics future_map;
+       double ret = 0.0;
+       TempoSection* new_section = copy_metrics_and_point (future_map, section);
+
+       if (solve_map (future_map, new_section, bpm, frame)) {
+               ret = new_section->pulse();
+       } else {
+               ret = pulse_at_frame_locked (_metrics, frame);
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       return ret;
+}
+
+void
+TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
+{
+       Metrics future_map;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
+               if (solve_map (future_map, new_section, bpm, frame)) {
+                       solve_map (_metrics, ts, bpm, frame);
+               }
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
        }
 
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double& beat)
+{
+       Metrics future_map;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
+               if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
+                       solve_map (_metrics, ts, bpm, beat);
+               }
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t&  frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, mt, frame);
+       }
+
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double&  beat)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
+       }
+
+       MetricPositionChanged (); // Emit Signal
+}
+
+TempoSection*
+TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
+{
+       TempoSection* t;
+       TempoSection* ret = 0;
+       MeterSection* m;
+
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (t == section) {
+                               if (t->position_lock_style() == MusicTime) {
+                                       ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
+                               } else {
+                                       ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
+                               }
+                               copy.push_back (ret);
+                               continue;
+                       }
+                       if (t->position_lock_style() == MusicTime) {
+                               copy.push_back (new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type()));
+                       } else {
+                               copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
+                       }
+               }
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       if (m->position_lock_style() == MusicTime) {
+                               copy.push_back (new MeterSection (m->pulse(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
+                       } else {
+                               copy.push_back (new MeterSection (m->frame(), m->divisions_per_bar(), m->note_divisor()));
+                       }
+               }
+       }
+       recompute_map (copy);
+       return ret;
+}
+
+bool
+TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
+{
+       Metrics copy;
+       TempoSection* new_section = 0;
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               new_section = copy_metrics_and_point (copy, ts);
+       }
+
+       double const beat = bbt_to_beats_locked (copy, bbt);
+       bool ret = solve_map (copy, new_section, bpm, beat);
+
+       Metrics::const_iterator d = copy.begin();
+       while (d != copy.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       return ret;
 }
 
 void
@@ -637,12 +1214,12 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
        Tempo newtempo (beats_per_minute, note_type);
        TempoSection* t;
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
                        {
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                        PropertyChanged (PropertyChange ());
                        break;
@@ -662,7 +1239,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
        /* find the TempoSection immediately preceding "where"
         */
 
-       for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
+       for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
 
                if ((*i)->frame() > where) {
                        break;
@@ -693,7 +1270,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
                Glib::Threads::RWLock::WriterLock lm (lock);
                /* cannot move the first tempo section */
                *((Tempo*)prev) = newtempo;
-               recompute_map (false);
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -704,13 +1281,13 @@ TempoMap::first_meter () const
 {
        const MeterSection *m = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
                        return *m;
                }
        }
 
-       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       fatal << _("programming error: no meter section in tempo map!") << endmsg;
        abort(); /*NOTREACHED*/
        return *m;
 }
@@ -722,7 +1299,7 @@ TempoMap::first_meter ()
 
        /* CALLER MUST HOLD LOCK */
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
                        return *m;
                }
@@ -740,7 +1317,7 @@ TempoMap::first_tempo () const
 
        /* CALLER MUST HOLD LOCK */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
                        return *t;
                }
@@ -756,7 +1333,7 @@ TempoMap::first_tempo ()
 {
        TempoSection *t = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
                        return *t;
                }
@@ -766,50 +1343,68 @@ TempoMap::first_tempo ()
        abort(); /*NOTREACHED*/
        return *t;
 }
-
 void
-TempoMap::require_map_to (framepos_t pos)
+TempoMap::recompute_tempos (Metrics& metrics)
 {
-       Glib::Threads::RWLock::WriterLock lm (lock);
+       TempoSection* prev_ts = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       if (_map.empty() || _map.back().frame < pos) {
-               extend_map (pos);
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t->position_lock_style() == AudioTime) {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
+
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
+
+                               }
+                       }
+                       prev_ts = t;
+               }
        }
 }
 
+/* tempos must be positioned correctly */
 void
-TempoMap::require_map_to (const BBT_Time& bbt)
+TempoMap::recompute_meters (Metrics& metrics)
 {
-       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;
+       MeterSection* prev_m = 0;
+       double accumulated_beats = 0.0;
+       uint32_t accumulated_bars = 0;
+       for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
+               if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
+                       if (prev_m) {
+                               const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
+                               accumulated_beats += beats_in_m;
+                               accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
+                       }
 
-       int additional_minutes = 1;
+                       if (meter->position_lock_style() == AudioTime) {
+                               pair<double, BBT_Time> pr;
+                               pr.first = pulse_at_frame_locked (metrics, meter->frame());
+                               BBT_Time const where = BBT_Time (accumulated_bars + 1, 1, 0);
+                               pr.second = where;
+                               meter->set_pulse (pr);
+                       } else {
+                               meter->set_frame (frame_at_pulse_locked (metrics, meter->pulse()));
+                       }
 
-       while (1) {
-               if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
-                       break;
+                       meter->set_beat (accumulated_beats);
+                       prev_m = meter;
                }
-               /* add some more distance, using bigger steps each time */
-               extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
-               additional_minutes *= 2;
        }
 }
 
 void
-TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
+TempoMap::recompute_map (Metrics& metrics, 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
@@ -817,481 +1412,792 @@ 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;
-               }
+       if (end == 0) {
+               /* silly call from Session::process() during startup
+                */
+               return;
        }
 
-       assert(meter);
+       recompute_tempos (metrics);
+       recompute_meters (metrics);
+}
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* ts;
+double
+TempoMap::pulse_at_beat (const double& beat) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return pulse_at_beat_locked (_metrics, beat);
+}
 
-               if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       tempo = ts;
-                       break;
+double
+TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms && m->beat() > beat) {
+                               break;
+                       }
+                       accumulated_beats = m->beat();
+                       prev_ms = m;
                }
+
        }
+       double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
+       return ret;
+}
 
-       assert(tempo);
+double
+TempoMap::beat_at_pulse (const double& pulse) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_pulse_locked (_metrics, pulse);
+}
 
-       /* assumes that the first meter & tempo are at frame zero */
-       current_frame = 0;
-       meter->set_frame (0);
-       tempo->set_frame (0);
+double
+TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
 
-       /* assumes that the first meter & tempo are at 1|1|0 */
-       current.bars = 1;
-       current.beats = 1;
-       current.ticks = 0;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms && m->pulse() > pulse) {
+                               break;
+                       }
+                       accumulated_beats = m->beat();
+                       prev_ms = m;
+               }
+       }
 
-       if (reassign_tempo_bbt) {
+       double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
 
-               MeterSection* rmeter = meter;
+       return beats_in_section + accumulated_beats;
+}
 
-               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
+TempoMetric
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
 
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       /* 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.
 
-                       TempoSection* ts;
-                       MeterSection* ms;
+          now see if we can find better candidates.
+       */
 
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
-                               /* reassign the BBT time of this tempo section
-                                * based on its bar offset position.
-                                */
+               if ((*i)->frame() > frame) {
+                       break;
+               }
 
-                               ts->update_bbt_time_from_bar_offset (*rmeter);
+               m.set_metric(*i);
 
-                       } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               rmeter = ms;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
-                       }
+               if (last) {
+                       *last = i;
                }
        }
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
+       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());
+
+       /* 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 */
 
-       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;
        }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
+       double const beat = beat_at_pulse_locked (_metrics, pulse_at_frame_locked (_metrics, frame + frame_off));
 
-       BBTPointList::const_iterator i = _map.end();
-       Metrics::iterator next_metric;
-
-       --i;
+       bbt = beats_to_bbt_locked (_metrics, beat);
+}
 
-       BBT_Time last_metric_start;
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       if ((*i).tempo->frame() > (*i).meter->frame()) {
-               last_metric_start = (*i).tempo->start();
-       } else {
-               last_metric_start = (*i).meter->start();
-       }
+       return bbt_to_beats_locked (_metrics, bbt);
+}
 
-       /* find the metric immediately after the tempo + meter sections for the
-        * last point in the map
-        */
+double
+TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
+{
+       /* CALLER HOLDS READ LOCK */
 
-       for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
-               if ((*next_metric)->start() > last_metric_start) {
-                       break;
+       double accumulated_beats = 0.0;
+       double accumulated_bars = 0.0;
+       MeterSection* prev_ms = 0;
+       /* because audio-locked meters have 'fake' integral beats,
+          there is no pulse offset here.
+       */
+       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;
+                               }
+                               accumulated_beats = m->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.
-        */
+       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);
 
-       _extend_map (const_cast<TempoSection*> ((*i).tempo),
-                    const_cast<MeterSection*> ((*i).meter),
-                    next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
+       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 (const double& beats)
 {
-       /* CALLER MUST HOLD WRITE LOCK */
-
-       TempoSection* ts;
-       MeterSection* ms;
-       double beat_frames;
-       double current_frame_exact;
-       framepos_t bar_start_frame;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
+       return beats_to_bbt_locked (_metrics, beats);
+}
 
-       if (current.beats == 1) {
-               bar_start_frame = current_frame;
-       } else {
-               bar_start_frame = 0;
-       }
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
+{
+       /* CALLER HOLDS READ LOCK */
+       MeterSection* prev_ms = 0;
+       const double beats = (b < 0.0) ? 0.0 : b;
+       uint32_t accumulated_bars = 0;
+       double accumulated_beats = 0.0;
 
-       beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
-       current_frame_exact = current_frame;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-       while (current_frame < end) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-               current.beats++;
-               current_frame_exact += beat_frames;
-               current_frame = llrint(current_frame_exact);
-
-               if (current.beats > meter->divisions_per_bar()) {
-                       current.bars++;
-                       current.beats = 1;
-               }
-
-               if (next_metric != metrics.end()) {
-
-                       /* no operator >= so invert operator < */
+                       if (prev_ms) {
+                               double const beats_to_m = m->beat() - prev_ms->beat();
+                               if (accumulated_beats + beats_to_m > beats) {
+                                       /* this is the meter after the one our beat is on*/
+                                       break;
+                               }
 
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
+                               /* we need a whole number of bars. */
+                               accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
+                               accumulated_beats += beats_to_m;
+                       }
 
-                       if (!(current < (*next_metric)->start())) {
+                       prev_ms = m;
+               }
+       }
 
-               set_metrics:
-                               if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
+       double const beats_in_ms = beats - accumulated_beats;
+       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;
 
-                                       tempo = ts;
+       BBT_Time ret;
 
-                                       /* 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.
-                                        */
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-                                       if (tempo->start().ticks != 0) {
+       /* 0 0 0 to 1 1 0 - based mapping*/
+       ++ret.bars;
+       ++ret.beats;
 
-                                               double next_beat_frames = tempo->frames_per_beat (_frame_rate);
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
+       }
 
-                                               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()));
+       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
+               ++ret.bars;
+               ret.beats = 1;
+       }
 
-                                               /* back up to previous beat */
-                                               current_frame_exact -= beat_frames;
-                                               current_frame = llrint(current_frame_exact);
+       return ret;
+}
 
-                                               /* 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)));
+Timecode::BBT_Time
+TempoMap::pulse_to_bbt (const double& pulse)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       MeterSection* prev_ms = 0;
+       uint32_t accumulated_bars = 0;
+       double accumulated_pulses = 0.0;
 
-                                               /* 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.
-                                                */
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-                                               double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                                               current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
-                                               current_frame = llrint(current_frame_exact);
+                       if (prev_ms) {
+                               double const pulses_to_m = m->pulse() - prev_ms->pulse();
+                               if (accumulated_pulses + pulses_to_m > pulse) {
+                                       /* this is the meter after the one our beat is on*/
+                                       break;
+                               }
 
-                                               /* 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));
+                               /* we need a whole number of bars. */
+                               accumulated_pulses += pulses_to_m;
+                               accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
+                       }
 
-                                       } else {
+                       prev_ms = m;
+               }
+       }
+       double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
+       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;
 
-                                               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);
-                                       }
+       BBT_Time ret;
 
-                               } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-                                       meter = ms;
+       /* 0 0 0 to 1 1 0 mapping*/
+       ++ret.bars;
+       ++ret.beats;
 
-                                       /* new meter section: always defines the
-                                        * start of a bar.
-                                        */
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
+       }
 
-                                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
-                                                                                      meter->start(), current, current_frame));
+       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
+               ++ret.bars;
+               ret.beats = 1;
+       }
 
-                                       assert (current.beats == 1);
+       return ret;
+}
 
-                                       meter->set_frame (current_frame);
-                               }
+double
+TempoMap::beat_at_frame (const framecnt_t& frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_frame_locked (_metrics, frame);
+}
 
-                               beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
+double
+TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+{
+       framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
+       double const pulse = pulse_at_frame_locked (metrics, offset_frame);
 
-                               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)));
+       return beat_at_pulse_locked (metrics, pulse);
+}
 
-                               ++next_metric;
+double
+TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+{
+       /* HOLD (at least) THE READER LOCK */
+       TempoSection* prev_ts = 0;
+       double accumulated_pulses = 0.0;
 
-                               if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
-                                       /* same position so go back and set this one up before advancing
-                                        */
-                                       goto set_metrics;
-                               }
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
+                       if (prev_ts && t->frame() > frame) {
+                               /*the previous ts is the one containing the frame */
+                               double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
+                               return ret;
                        }
+                       accumulated_pulses = t->pulse();
+                       prev_ts = t;
                }
+       }
 
-               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));
-               }
+       /* treated as constant for this ts */
+       double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
 
-               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;
-                       }
-               }
-       }
+       return pulses_in_section + accumulated_pulses;
 }
 
-TempoMetric
-TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+framecnt_t
+TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       TempoMetric m (first_meter(), first_tempo());
+       return frame_at_beat_locked (_metrics, beat);
+}
 
-       /* 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.
+framecnt_t
+TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
+       frameoffset_t const frame_off = frame_offset_at (metrics, frame);
+       return frame - frame_off;
+}
 
-          now see if we can find better candidates.
-       */
+framecnt_t
+TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       /* HOLD THE READER LOCK */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       const TempoSection* prev_ts = 0;
+       double accumulated_pulses = 0.0;
 
-               if ((*i)->frame() > frame) {
-                       break;
-               }
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-               m.set_metric(*i);
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts && t->pulse() > pulse) {
+                               return prev_ts->frame_at_pulse (pulse, _frame_rate);
+                       }
 
-               if (last) {
-                       *last = i;
+                       accumulated_pulses = t->pulse();
+                       prev_ts = t;
                }
        }
+       /* must be treated as constant, irrespective of _type */
+       double const pulses_in_section = pulse - accumulated_pulses;
+       double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
 
-       return m;
+       framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
+
+       return ret;
 }
 
-TempoMetric
-TempoMap::metric_at (BBT_Time bbt) const
+double
+TempoMap::pulse_offset_at (const Metrics& metrics, const double& pulse) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       TempoMetric m (first_meter(), first_tempo());
+       MeterSection* prev_m = 0;
+       double pulse_off = first_meter().pulse();
 
-       /* 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.
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_m) {
+                               if (m->pulse() > pulse) {
+                                       break;
+                               }
 
-          now see if we can find better candidates.
-       */
+                               if (m->position_lock_style() == AudioTime) {
+                                       pulse_off += (m->pulse() - prev_m->pulse()) - floor (m->pulse() - prev_m->pulse());
+                               }
+                       }
+                       prev_m = m;
+               }
+       }
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       return pulse_off;
+}
 
-               BBT_Time section_start ((*i)->start());
+frameoffset_t
+TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
+{
+       frameoffset_t frame_off = 0;
 
-               if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
-                       break;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (m->frame() > frame) {
+                               break;
+                       }
+                       if (m->position_lock_style() == AudioTime) {
+                               frame_off += frame_at_pulse_locked (metrics, m->pulse()) - m->frame();
+                       }
                }
-
-               m.set_metric (*i);
        }
 
-       return m;
+       return frame_off;
 }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
+framepos_t
+TempoMap::frame_time (const BBT_Time& bbt)
 {
-       require_map_to (frame);
+       if (bbt.bars < 1) {
+               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);
+       double const beat = bbt_to_beats_locked (_metrics, bbt);
+       framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
+       return frame;
+}
 
-       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;
+framepos_t
+TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
+{
+       /* HOLD THE READER LOCK */
+
+       framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
+
+       return ret;
+}
+
+bool
+TempoMap::check_solved (Metrics& metrics, bool by_frame)
+{
+       TempoSection* prev_ts = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
+                                       return false;
+                               }
+                               if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
+                                       return false;
+                               }
+                               /*
+                               if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
+                                       std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
+                                               " |error          :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
+                                               "|frame at beat   :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
+                                               " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
+                                       return false;
+                               }
+                               */
+                       }
+                       prev_ts = t;
+               }
        }
 
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
+       return true;
 }
 
-void
-TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       TempoSection* prev_ts = 0;
+       TempoSection* section_prev = 0;
+       MetricSectionFrameSorter fcmp;
+       MetricSectionSorter cmp;
 
-       if (!lm.locked()) {
-               throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
+       section->set_frame (frame);
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t == section) {
+                                       section_prev = prev_ts;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
+                               }
+                       }
+                       prev_ts = t;
+               }
        }
 
-       if (_map.empty() || _map.back().frame < frame) {
-               throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
+       if (section_prev) {
+               section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), section->pulse(), _frame_rate));
+               section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
        }
 
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
-}
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
-{
-       /* CALLER MUST HOLD READ LOCK */
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
 
-       bbt.bars = (*i).bar;
-       bbt.beats = (*i).beat;
+       imaginary.sort (fcmp);
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
 
-       if ((*i).frame == frame) {
-               bbt.ticks = 0;
+       imaginary.sort (cmp);
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
        } else {
-               bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
-                                   BBT_Time::ticks_per_beat);
+               recompute_tempos (imaginary);
+       }
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
        }
+       //dump (imaginary, std::cerr);
+
+       return false;
 }
 
-framepos_t
-TempoMap::frame_time (const BBT_Time& bbt)
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
 {
-       if (bbt.bars < 1) {
-               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
-               return 0;
-       }
+       MetricSectionSorter cmp;
+       MetricSectionFrameSorter fcmp;
+       TempoSection* prev_ts = 0;
+       TempoSection* section_prev = 0;
 
-       if (bbt.beats < 1) {
-               throw std::logic_error ("beats are counted from one");
+       section->set_pulse (pulse);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t == section) {
+                                       section_prev = prev_ts;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
+                               }
+                       }
+                       prev_ts = t;
+               }
+       }
+       if (section_prev) {
+               section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
+               section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
        }
 
-       require_map_to (bbt);
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       imaginary.sort (cmp);
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
 
-       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));
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
 
-       if (bbt.ticks != 0) {
-               return ((*e).frame - (*s).frame) +
-                       llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
+       imaginary.sort (fcmp);
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
        } else {
-               return ((*e).frame - (*s).frame);
+               recompute_tempos (imaginary);
        }
-}
 
-framecnt_t
-TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
-{
-       BBT_Time when;
-       bbt_time (pos, when);
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_duration_at_unlocked (when, bbt, dir);
+       //dump (imaginary, std::cerr);
+
+       return false;
 }
 
-framecnt_t
-TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
+void
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
 {
-       if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
-               return 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);
+       MeterSection* prev_ms = 0;
 
-       assert (wi != _map.end());
+       pair<double, BBT_Time> b_bbt = make_pair (pulse, BBT_Time (1, 1, 0));
+       double accumulated_beats = 0.0;
+       uint32_t accumulated_bars = 0;
 
-       uint32_t bars = 0;
-       uint32_t beats = 0;
+       section->set_pulse (b_bbt);
 
-       while (wi != _map.end() && bars < bbt.bars) {
-               ++wi;
-               if ((*wi).is_bar()) {
-                       ++bars;
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
+                               accumulated_beats += beats_in_m;
+                               accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
+                       }
+                       if (m == section){
+                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                               b_bbt = make_pair (pulse, BBT_Time (accumulated_bars + 1, 1, 0));
+                               section->set_pulse (b_bbt);
+                               m->set_beat (accumulated_beats);
+                               prev_ms = section;
+                               continue;
+                       }
+                       if (prev_ms) {
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
+                               } else {
+                                       pair<double, BBT_Time> b_bbt = make_pair (pulse_at_frame_locked (imaginary, m->frame()), BBT_Time (accumulated_bars + 1, 1, 0));
+                                       m->set_pulse (b_bbt);
+                               }
+                       }
+                       m->set_beat (accumulated_beats);
+                       prev_ms = m;
                }
        }
-       assert (wi != _map.end());
 
-       while (wi != _map.end() && beats < bbt.beats) {
-               ++wi;
-               ++beats;
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_meters (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_meters (imaginary);
        }
-       assert (wi != _map.end());
+}
 
-       /* add any additional frames related to ticks in the added value */
+void
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
+       uint32_t accumulated_bars = 0;
+
+       section->set_frame (frame);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
+                               accumulated_beats += beats_in_m;
+                               accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
+                       }
+                       if (m == section){
+                               /*
+                                 here we define the pulse for this frame.
+                                 we're going to set it 'incorrectly' to the next integer and use this 'error'
+                                 as an offset to the map as far as users of the public methods are concerned.
+                                 (meters should go on absolute pulses to keep us sane)
+                               */
+                               double const pulse_at_f = pulse_at_frame_locked (imaginary, m->frame());
+                               pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, BBT_Time (accumulated_bars + 1, 1, 0));
+                               m->set_pulse (b_bbt);
+                               m->set_beat (accumulated_beats);
+                               prev_ms = m;
+                               continue;
+                       }
+                       if (prev_ms) {
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
+                               } else {
+                                       double const pulse_at_f = pulse_at_frame_locked (imaginary, frame);
+                                       pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, BBT_Time (accumulated_bars + 1, 1, 0));
+                                       m->set_pulse (b_bbt);
+                               }
+                       }
+                       m->set_beat (accumulated_beats);
+                       prev_ms = m;
+               }
+       }
 
-       if (bbt.ticks != 0) {
-               return ((*wi).frame - (*start).frame) +
-                       (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_meters (imaginary);
+               section->set_position_lock_style (MusicTime);
        } else {
-               return ((*wi).frame - (*start).frame);
+               recompute_meters (imaginary);
        }
 }
 
+framecnt_t
+TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
+       double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
+       double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
+       framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
+       framecnt_t const ret = time_at_bbt;
+
+       return ret;
+}
+
 framepos_t
 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
 {
@@ -1307,52 +2213,39 @@ 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;
+       uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
+       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;
 
-       bbt_time (fr, the_beat, i);
-
-       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,
@@ -1360,280 +2253,249 @@ 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);
+       framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+
+       return ret_frame;
 }
 
 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);
-       }
-
-       assert (fi != _map.end());
 
-       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")));
+       double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
+       BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
 
        switch (type) {
        case Bar:
                if (dir < 0) {
                        /* find bar previous to 'frame' */
-
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundDownMaybe) {
-                                       return frame;
-                               }
-                               --fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               if (fi == _map.begin()) {
-                                       break;
-                               }
-                               fi--;
-                       }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
 
                } else if (dir > 0) {
-
                        /* find bar following 'frame' */
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundUpMaybe) {
-                                       return frame;
-                               }
-                               ++fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               fi++;
-                               if (fi == _map.end()) {
-                                       --fi;
-                                       break;
-                               }
-                       }
-
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
-
+                       ++bbt.bars;
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
                } else {
-
                        /* true rounding: find nearest bar */
-
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
-
-                       while ((*prev).beat != 1) {
-                               if (prev == _map.begin()) {
-                                       break;
-                               }
-                               prev--;
-                       }
-
-                       while ((next != _map.end()) && (*next).beat != 1) {
-                               next++;
-                       }
-
-                       if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
+                       framepos_t raw_ft = frame_time (bbt);
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       framepos_t prev_ft = frame_time (bbt);
+                       ++bbt.bars;
+                       framepos_t next_ft = frame_time (bbt);
+
+                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
+                               return next_ft;
                        } else {
-                               return (*next).frame;
+                               return prev_ft;
                        }
-
                }
 
                break;
 
        case Beat:
                if (dir < 0) {
-
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
-
-                       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;
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
                } 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;
+                       return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
                } else {
-                       /* find beat nearest to frame */
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
-
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       /* 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;
-                       }
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
                }
                break;
        }
 
-       abort(); /* NOTREACHED */
        return 0;
 }
 
 void
-TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
-                   TempoMap::BBTPointList::const_iterator& end,
+TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    framepos_t lower, framepos_t upper)
 {
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               if (_map.empty() || (_map.back().frame < upper)) {
-                       recompute_map (false, upper);
-               }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
+       int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
+       /* although the map handles negative beats, bbt doesn't. */
+       if (cnt < 0.0) {
+               cnt = 0.0;
        }
+       while (cnt <= upper_beat) {
+               framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
+               TempoSection const tempo = tempo_section_at_locked (pos);
+               MeterSection const meter = meter_section_at_locked (pos);
+               BBT_Time const bbt = beats_to_bbt (cnt);
 
-       begin = lower_bound (_map.begin(), _map.end(), lower);
-       end = upper_bound (_map.begin(), _map.end(), upper);
+               points.push_back (BBTPoint (meter, Tempo (tempo.beats_per_minute(), tempo.note_type()), pos, bbt.bars, bbt.beats));
+               ++cnt;
+       }
 }
 
 const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return tempo_section_at_locked (frame);
+}
+
+const TempoSection&
+TempoMap::tempo_section_at_locked (framepos_t frame) const
+{
+       Metrics::const_iterator i;
+       TempoSection* prev = 0;
+
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if (t->frame() > frame) {
+                               break;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       return *prev;
+}
+
+
+/* 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 (const framepos_t& frame, const framecnt_t& sr) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       const TempoSection* ts_at = &tempo_section_at_locked (frame);
+       const TempoSection* ts_after = 0;
        Metrics::const_iterator i;
-       TempoSection* prev = 0;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
                        if ((*i)->frame() > frame) {
+                               ts_after = t;
                                break;
                        }
-
-                       prev = t;
                }
        }
 
-       if (prev == 0) {
-               fatal << endmsg;
-               abort(); /*NOTREACHED*/
+       if (ts_after) {
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
        }
-
-       return *prev;
+       /* must be treated as constant tempo */
+       return ts_at->frames_per_beat (_frame_rate);
 }
 
-const Tempo&
-TempoMap::tempo_at (framepos_t frame) const
+const Tempo
+TempoMap::tempo_at (const framepos_t& frame) const
 {
-       TempoMetric m (metric_at (frame));
-       return m.tempo();
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       //frameoffset_t const frame_off = frame_offset_at (_metrics, 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) {
+                               /* t is the section past frame */
+                               double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
+                               Tempo const ret_tempo (ret, prev_ts->note_type());
+                               return ret_tempo;
+                       }
+                       prev_ts = t;
+               }
+       }
+
+       double const ret = prev_ts->beats_per_minute();
+       Tempo const ret_tempo (ret, prev_ts->note_type ());
+
+       return ret_tempo;
 }
 
 const MeterSection&
 TempoMap::meter_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_locked (frame);
+}
+
+const MeterSection&
+TempoMap::meter_section_at_locked (framepos_t frame) const
+{
+       //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
        Metrics::const_iterator i;
        MeterSection* prev = 0;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                MeterSection* t;
 
                if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                       if ((*i)->frame() > frame) {
+                       if (prev && (*i)->frame() > frame) {
                                break;
                        }
 
@@ -1664,7 +2526,7 @@ TempoMap::get_state ()
 
        {
                Glib::Threads::RWLock::ReaderLock lm (lock);
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
+               for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                        root->add_child_nocopy ((*i)->get_state());
                }
        }
@@ -1680,9 +2542,9 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                XMLNodeList nlist;
                XMLNodeConstIterator niter;
-               Metrics old_metrics (metrics);
+               Metrics old_metrics (_metrics);
                MeterSection* last_meter = 0;
-               metrics.clear();
+               _metrics.clear();
 
                nlist = node.children();
 
@@ -1693,18 +2555,18 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        TempoSection* ts = new TempoSection (*child);
-                                       metrics.push_back (ts);
+                                       _metrics.push_back (ts);
 
                                        if (ts->bar_offset() < 0.0) {
                                                if (last_meter) {
-                                                       ts->update_bar_offset_from_bbt (*last_meter);
+                                                       //ts->update_bar_offset_from_bbt (*last_meter);
                                                }
                                        }
                                }
 
                                catch (failed_constructor& err){
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
 
@@ -1712,13 +2574,13 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        MeterSection* ms = new MeterSection (*child);
-                                       metrics.push_back (ms);
+                                       _metrics.push_back (ms);
                                        last_meter = ms;
                                }
 
                                catch (failed_constructor& err) {
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
                        }
@@ -1726,26 +2588,47 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                if (niter == nlist.end()) {
                        MetricSectionSorter cmp;
-                       metrics.sort (cmp);
-               }
+                       _metrics.sort (cmp);
+               }
+               /* check for legacy sessions where bbt was the base musical unit for tempo */
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+                       MeterSection* prev_ms;
+                       TempoSection* prev_ts;
+                       if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               if (prev_ms->pulse() < 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_pulse (start);
+                               }
+                       } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               if (prev_ts->pulse() < 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_pulse (start);
 
+                               }
+                       }
+               }
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
 
-               Metrics::iterator prev = metrics.end();
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-                       if (prev != metrics.end()) {
-                               if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
-                                               error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
+               Metrics::iterator prev = _metrics.end();
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+                       if (prev != _metrics.end()) {
+                               MeterSection* ms;
+                               MeterSection* prev_ms;
+                               TempoSection* ts;
+                               TempoSection* prev_ts;
+                               if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (prev_ms->pulse() == ms->pulse()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
                                                return -1;
                                        }
-                               } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
-                                               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->pulse() == ts->pulse()) {
+                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
+                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
                                                return -1;
                                        }
                                }
@@ -1753,7 +2636,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        prev = i;
                }
 
-               recompute_map (true, -1);
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -1762,22 +2645,30 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 }
 
 void
-TempoMap::dump (std::ostream& o) const
+TempoMap::dump (const Metrics& metrics, std::ostream& o) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
        const MeterSection* m;
        const TempoSection* t;
+       const TempoSection* prev_ts = 0;
 
        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? "
-                         << t->movable() << ')' << endl;
+                       o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
+                         << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
+                       o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
+                       if (prev_ts) {
+                               o << "previous     : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
+                               o << "calculated   : " << prev_ts->tempo_at_pulse (t->pulse()) *  prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
+                       }
+                       prev_ts = t;
                } 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()
-                         << " (movable? " << m->movable() << ')' << endl;
+                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
+                         << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
                }
        }
+       o << "------" << std::endl;
 }
 
 int
@@ -1786,7 +2677,7 @@ TempoMap::n_tempos() const
        Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<const TempoSection*>(*i) != 0) {
                        cnt++;
                }
@@ -1801,7 +2692,7 @@ TempoMap::n_meters() const
        Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<const MeterSection*>(*i) != 0) {
                        cnt++;
                }
@@ -1815,7 +2706,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        if ((*i)->frame() >= where && (*i)->movable ()) {
                                (*i)->set_frame ((*i)->frame() + amount);
                        }
@@ -1843,52 +2734,72 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                bool first = true;
                MetricSection* prev = 0;
 
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
+               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_pulse (t->pulse());
+                                       }
+                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+                                               ts->set_pulse (m->pulse());
+                                       }
+                                       ts->set_frame (prev->frame());
+
+                               }
+                               if (ms) {
+                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+                                               pair<double, BBT_Time> start = make_pair (m->pulse(), m->bbt());
+                                               ms->set_pulse (start);
+                                       }
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               pair<double, BBT_Time> start = make_pair (t->pulse(), beats_to_bbt_locked (_metrics, t->pulse()));
+                                               ms->set_pulse (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 {
+                       // cerr << bbt << endl;
 
-                               if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
-                                       /* round up to next beat */
-                                       bbt.beats += 1;
-                               }
+                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
+                               tempo = t;
+                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
+                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               bbt_time (m->frame(), bbt);
 
-                               bbt.ticks = 0;
+                               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
 
-                               if (bbt.beats != 1) {
-                                       /* round up to next bar */
-                                       bbt.bars += 1;
-                                       bbt.beats = 1;
-                               }
-                       }
+                               if (first) {
+                                       first = false;
+                               } else {
 
-                       // cerr << bbt << endl;
+                                       if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
+                                               /* round up to next beat */
+                                               bbt.beats += 1;
+                                       }
 
-                       (*i)->set_start (bbt);
+                                       bbt.ticks = 0;
 
-                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
-                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (bbt.beats != 1) {
+                                               /* round up to next bar */
+                                               bbt.bars += 1;
+                                               bbt.beats = 1;
+                                       }
+                               }
+                               pair<double, BBT_Time> start = make_pair (pulse_at_frame_locked (_metrics, m->frame()), bbt);
+                               m->set_pulse (start);
                                meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                abort(); /*NOTREACHED*/
@@ -1897,7 +2808,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        prev = (*i);
                }
 
-               recompute_map (true);
+               recompute_map (_metrics);
        }
 
 
@@ -1916,7 +2827,7 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
        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) {
+               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);
@@ -1952,12 +2863,12 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
 
                //remove all the remaining metrics
                for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
-                       metrics.remove(*i);
+                       _metrics.remove(*i);
                        moved = true;
                }
 
                if (moved) {
-                       recompute_map (true);
+                       recompute_map (_metrics);
                }
        }
        PropertyChanged (PropertyChange ());
@@ -1970,205 +2881,14 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
 framepos_t
 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       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;
-
-               if ((t = dynamic_cast<const TempoSection*>(*next_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 = (*next_tempo)->frame ();
-
-                       if (pos < 0 && f == 0) {
-                               f = pos;
-                       }
-
-                       if (f > pos) {
-                               break;
-                       }
-
-                       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)));
-
-                       while (next_tempo != metrics.end ()) {
-
-                               ++next_tempo;
-
-                               if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return pos;
+       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 */
@@ -2176,135 +2896,24 @@ framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_iterator i;
-       const MeterSection* meter;
-       const MeterSection* m;
-       const TempoSection* tempo;
-       const TempoSection* t;
-       double frames_per_beat;
-       framepos_t effective_pos = max (pos, (framepos_t) 0);
-
-       meter = &first_meter ();
-       tempo = &first_tempo ();
-
-       assert (meter);
-       assert (tempo);
-
-       /* find the starting metrics for tempo & meter */
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-
-               if ((*i)->frame() > effective_pos) {
-                       break;
-               }
-
-               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       tempo = t;
-               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       meter = m;
-               }
-       }
-
-       /* We now have:
-
-          meter -> the Meter for "pos"
-          tempo -> the Tempo for "pos"
-          i     -> for first new metric after "pos", possibly metrics.end()
-       */
-
-       /* now comes the complicated part. we have to add one beat a time,
-          checking for a new metric on every beat.
-       */
 
-       frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
-       uint64_t bars = 0;
-
-       while (op.bars) {
-
-               bars++;
-               op.bars--;
-
-               /* check if we need to use a new metric section: has adding frames moved us
-                  to or after the start of the next metric section? in which case, use it.
-               */
-
-               if (i != metrics.end()) {
-                       if ((*i)->frame() <= pos) {
-
-                               /* about to change tempo or meter, so add the
-                                * number of frames for the bars we've just
-                                * traversed before we change the
-                                * frames_per_beat value.
-                                */
-
-                               pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
-                               bars = 0;
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       tempo = t;
-                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                                       meter = m;
-                               }
-                               ++i;
-                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
-                       }
-               }
-
-       }
-
-       pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
-
-       uint64_t beats = 0;
-
-       while (op.beats) {
-
-               /* given the current meter, have we gone past the end of the bar ? */
-
-               beats++;
-               op.beats--;
-
-               /* check if we need to use a new metric section: has adding frames moved us
-                  to or after the start of the next metric section? in which case, use it.
-               */
-
-               if (i != metrics.end()) {
-                       if ((*i)->frame() <= pos) {
-
-                               /* about to change tempo or meter, so add the
-                                * number of frames for the beats we've just
-                                * traversed before we change the
-                                * frames_per_beat value.
-                                */
-
-                               pos += llrint (beats * frames_per_beat);
-                               beats = 0;
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       tempo = t;
-                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                                       meter = m;
-                               }
-                               ++i;
-                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
-                       }
-               }
+       BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
+       pos_bbt.ticks += op.ticks;
+       if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
+               ++pos_bbt.beats;
+               pos_bbt.ticks -= BBT_Time::ticks_per_beat;
        }
-
-       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_bbt.beats += op.beats;
+       /* the meter in effect will start on the bar */
+       double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
+       while (pos_bbt.beats >= divisions_per_bar + 1) {
+               ++pos_bbt.bars;
+               divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
+               pos_bbt.beats -= divisions_per_bar;
        }
+       pos_bbt.bars += op.bars;
 
-       return pos;
+       return frame_time_locked (_metrics, pos_bbt);
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
@@ -2313,126 +2922,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 {
@@ -2441,40 +2931,6 @@ 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&
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
@@ -2488,7 +2944,7 @@ operator<< (std::ostream& o, const Tempo& t) {
 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;
@@ -2496,7 +2952,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;