Tempo ramps -update audio-locked meter bbt correctly, a bit more explanation.
[ardour.git] / libs / ardour / tempo.cc
index 50ec59264cf208959bc0cd752961bb1bde828706..45c26b8c361015ce657e607bf5f23a8e25f55f13 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,46 @@ 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())
+       , _c_func (0.0)
+       , _active (true)
 {
-       XMLProperty const * prop;
-       BBT_Time start;
+       const XMLProperty *prop;
        LocaleGuard lg;
-
-       if ((prop = node.property ("start")) == 0) {
-               error << _("TempoSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
+       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;
@@ -118,23 +141,23 @@ TempoSection::TempoSection (const XMLNode& node)
 
        set_movable (string_is_affirmative (prop->value()));
 
-       if ((prop = node.property ("bar-offset")) == 0) {
-               _bar_offset = -1.0;
+       if ((prop = node.property ("active")) == 0) {
+               warning << _("TempoSection XML node has no \"active\" property") << endmsg;
+               set_active(true);
        } else {
-               if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
-                       error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
-                       throw failed_constructor();
-               }
+               set_active (string_is_affirmative (prop->value()));
        }
 
        if ((prop = node.property ("tempo-type")) == 0) {
-               _type = TempoSectionType::Constant;
+               _type = Constant;
        } else {
-               if (strstr(prop->value().c_str(),"Constant")) {
-                       _type = TempoSectionType::Constant;
-               } else {
-                       _type = TempoSectionType::Ramp;
-               }
+               _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())));
        }
 }
 
@@ -145,195 +168,266 @@ TempoSection::get_state() const
        char buf[256];
        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);
        root->add_property ("note-type", buf);
-       // snprintf (buf, sizeof (buf), "%f", _bar_offset);
-       // root->add_property ("bar-offset", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
-
-       snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
-       root->add_property ("tempo-type", buf);
+       snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
+       root->add_property ("active", buf);
+       root->add_property ("tempo-type", enum_2_string (_type));
+       root->add_property ("lock-style", enum_2_string (position_lock_style()));
 
        return *root;
 }
 
 void
-
-TempoSection::update_bar_offset_from_bbt (const Meter& m)
-{
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
-               (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()));
-}
-
-void
-TempoSection::set_type (TempoSectionType type)
+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 (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
 
        if (_type == Constant) {
-               return beats_per_minute();
+               return pulses_per_minute();
        }
 
-       return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
+       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 (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
 {
        if (_type == Constant) {
-               return 0;
+               return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
        }
 
-       return minute_to_frame (time_at_tick_tempo (tempo *  BBT_Time::ticks_per_beat,  end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
+       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::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::tempo_at_pulse (const double& p) const
 {
+
        if (_type == Constant) {
-               return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
+               return pulses_per_minute();
        }
-
-       return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
+       double const ppm = pulse_tempo_at_pulse (p - pulse());
+       return ppm;
 }
 
-framepos_t
-TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** 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) {
-               return (framepos_t) floor ((tick  / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
+               double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+               return  beats;
        }
 
-       return minute_to_frame (time_at_tick (tick, end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
+       return pulse_at_pulse_tempo (ppm) + pulse();
 }
 
-double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** 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
 {
-       return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
-}
+       if (_type == Constant) {
+               return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+       }
 
-framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
-{
-       return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
+       return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
 }
 
-framecnt_t
-TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+/** 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
 {
-       return time * 60.0 * frame_rate;
+       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::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
+TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
 {
-       return (frame / (double) frame_rate) / 60.0;
+       double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
+       return pulses_per_minute() *  (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
 }
 
-/* constant for exp */
+/* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
 double
-TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
+TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
 {
-       return log (end_tpm / ticks_per_minute()) /  c_func (end_tpm, end_time);
+       return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
 }
-double
-TempoSection::c_func (double end_tpm, double end_time) const
+
+framecnt_t
+TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
 {
-       return log (end_tpm / ticks_per_minute()) /  end_time;
+       return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
 }
 
-/* tempo in tpm at time in minutes */
 double
-TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
+TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
 {
-       return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
+       return (frame / (double) frame_rate) / 60.0;
 }
 
-/* time in minutes at tempo in tpm */
+/* position function */
 double
-TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
+TempoSection::a_func (double end_bpm, double c_func) const
 {
-       return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
+       return log (end_bpm / pulses_per_minute()) /  c_func;
 }
 
-/* tempo in bpm at time in minutes */
+/*function constant*/
 double
-TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
+TempoSection::c_func (double end_bpm, double end_time) const
 {
-       return tick_tempo_at_time (time, end_bpm *  BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
+       return log (end_bpm / pulses_per_minute()) /  end_time;
 }
 
-/* time in minutes at tempo in bpm */
+/* tempo in ppm at time in minutes */
 double
-TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
+TempoSection::pulse_tempo_at_time (const double& time) const
 {
-       return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
+       return exp (_c_func * time) * pulses_per_minute();
 }
 
-/* tick at time in minutes */
+/* time in minutes at tempo in ppm */
 double
-TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
+TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
 {
-       return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
+       return log (pulse_tempo / pulses_per_minute()) / _c_func;
 }
 
-/* time in minutes at tick */
+/* tick at tempo in ppm */
 double
-TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
+TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
 {
-       return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
+       return (pulse_tempo - pulses_per_minute()) / _c_func;
 }
 
-/* beat at time in minutes */
+/* tempo in ppm at tick */
 double
-TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
+TempoSection::pulse_tempo_at_pulse (const double& pulse) const
 {
-       return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
+       return (pulse * _c_func) + pulses_per_minute();
 }
 
-/* time in munutes at beat */
+/* pulse at time in minutes */
 double
-TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
+TempoSection::pulse_at_time (const double& time) const
 {
-       return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
+       return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
 }
 
-void
-TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
+/* time in minutes at pulse */
+double
+TempoSection::time_at_pulse (const double& pulse) const
 {
-       BBT_Time new_start;
-
-       if (_bar_offset < 0.0) {
-               /* not set yet */
-               return;
-       }
-
-       new_start.bars = start().bars;
-
-       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;
-       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));
-
-       set_start (new_start);
+       return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
 }
 
 /***********************************************************************/
@@ -341,26 +435,67 @@ 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) {
+       const XMLProperty *prop;
+       BBT_Time bbt;
+       double pulse = 0.0;
+       double beat = 0.0;
+       framepos_t frame = 0;
+       pair<double, BBT_Time> start;
+
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+               } else {
+                       /* legacy session - start used to be in bbt*/
+                       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_pulse (pulse);
 
-       set_start (start);
+       if ((prop = node.property ("beat")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
+                       error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
+               }
+       }
+
+       start.first = beat;
+
+       if ((prop = node.property ("bbt")) == 0) {
+               error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
+       } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+               error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
+               //throw failed_constructor();
+       }
+
+       start.second = bbt;
+       set_beat (start);
+
+       if ((prop = node.property ("frame")) != 0) {
+               if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
+                       error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
+               } else {
+                       set_frame (frame);
+               }
+       }
 
        /* beats-per-bar is old; divisions-per-bar is new */
 
@@ -370,9 +505,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();
        }
 
@@ -380,12 +514,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();
@@ -401,13 +541,20 @@ MeterSection::get_state() const
        char buf[256];
        LocaleGuard lg;
 
+       snprintf (buf, sizeof (buf), "%lf", pulse());
+       root->add_property ("pulse", buf);
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 start().bars,
-                 start().beats,
-                 start().ticks);
-       root->add_property ("start", buf);
+                 bbt().bars,
+                 bbt().beats,
+                 bbt().ticks);
+       root->add_property ("bbt", buf);
+       snprintf (buf, sizeof (buf), "%lf", beat());
+       root->add_property ("beat", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
+       snprintf (buf, sizeof (buf), "%li", frame());
+       root->add_property ("frame", buf);
+       root->add_property ("lock-style", enum_2_string (position_lock_style()));
        snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
        root->add_property ("divisions-per-bar", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
@@ -417,32 +564,69 @@ MeterSection::get_state() const
 }
 
 /***********************************************************************/
+/*
+  Tempo Map Overview
 
+  Tempos can be thought of as a source of the musical pulse.
+
+  Note that Tempo::beats_per_minute() has nothing to do with musical beats.
+  It should rather be thought of as tempo note divisions per minute.
+
+  TempoSections, which are nice to think of in whole pulses per minute,
+  and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
+  and beats (via note_divisor) are used to form a tempo map.
+  TempoSections and MeterSections may be locked to either audio or music (position lock style).
+  We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
+  We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
+
+  Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
+
+  The first tempo and first meter are special. they must move together, and must be locked to audio.
+  Audio locked tempos which lie before the first meter are made inactive.
+  They will be re-activated if the first meter is again placed before them.
+
+  Both tempos and meters have a pulse position and a frame position.
+  Meters also have a beat position, which is always 0.0 for the first meter.
+
+  A tempo locked to music is locked to musical pulses.
+  A meter locked to music is locked to beats.
+
+  Recomputing the tempo map is the process where the 'missing' position
+  (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
+
+  It is important to keep the _metrics in an order that makes sense.
+  Because ramped MusicTime and AudioTime tempos can interact with each other,
+  reordering is frequent. Care must be taken to keep _metrics in a solved state.
+  Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
+*/
 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;
+       BBT_Time start (1, 1, 0);
 
-       start.bars = 1;
-       start.beats = 1;
-       start.ticks = 0;
-
-       TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::TempoSectionType::Ramp);
-       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, 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 ()
@@ -458,7 +642,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);
                        }
                }
        }
@@ -473,11 +657,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;
                                }
                        }
@@ -496,7 +680,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);
                        }
                }
        }
@@ -511,11 +695,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;
                                }
                        }
@@ -529,34 +713,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,
@@ -564,50 +739,55 @@ 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));
+                                       (*i)->set_position_lock_style (AudioTime);
                                        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 (!(*i)->movable()) {
+                       if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
+
+                               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);
 
                                }
 
@@ -623,35 +803,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, TempoSection::TempoSectionType type)
+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, type);
+                       add_tempo_locked (tempo, pulse, true, type);
                } else {
+                       first.set_type (type);
                        {
                                /* cannot move the first tempo section */
-                               first.set_type (type);
                                *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -660,58 +858,73 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::TempoSectionType type)
+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, type);
+               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);
+                       first.set_pulse (0.0);
+                       first.set_position_lock_style (AudioTime);
+                       {
+                               /* 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, ARDOUR::TempoSection::TempoSectionType type)
+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(), 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());
        }
 }
 
@@ -722,31 +935,83 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
                Glib::Threads::RWLock::WriterLock lm (lock);
                MeterSection& first (first_meter());
 
-               if (ms.start() != first.start()) {
+               if (ms.movable()) {
+                       remove_meter_locked (ms);
+                       add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
+               } else {
+                       const PositionLockStyle pl = ms.position_lock_style();
+                       /* cannot move the first meter section */
+                       *static_cast<Meter*>(&first) = meter;
+                       first.set_position_lock_style (pl);
+                       recompute_map (_metrics);
+               }
+       }
+
+       PropertyChanged (PropertyChange ());
+}
+
+void
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+
+               const double beat = ms.beat();
+               const BBT_Time bbt = ms.bbt();
+               if (ms.movable()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, where, true);
+                       add_meter_locked (meter, frame, beat, bbt, true);
                } else {
+                       MeterSection& first (first_meter());
+                       TempoSection& first_t (first_tempo());
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
-                       recompute_map (true);
+                       first.set_position_lock_style (AudioTime);
+                       first.set_pulse (0.0);
+                       first.set_frame (frame);
+                       pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
+                       first.set_beat (beat);
+                       first_t.set_frame (first.frame());
+                       first_t.set_pulse (0.0);
+                       first_t.set_position_lock_style (AudioTime);
+
+                       recompute_map (_metrics);
                }
        }
+       PropertyChanged (PropertyChange ());
+}
+
+
+void
+TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               add_meter_locked (meter, beat, where, true);
+       }
+
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (_metrics, std::cerr);
+       }
+#endif
 
        PropertyChanged (PropertyChange ());
 }
 
 void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, where, true);
+               add_meter_locked (meter, frame, beat, where, true);
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (std::cerr);
+               dump (_metrics, std::cerr);
        }
 #endif
 
@@ -754,7 +1019,7 @@ 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_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
@@ -767,58 +1032,296 @@ TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
                where.beats = 1;
                where.bars++;
        }
-
        /* new meters *always* start on a beat. */
        where.ticks = 0;
+       double pulse = pulse_at_beat_locked (_metrics, beat);
 
-       do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
+       MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
+       do_insert (new_meter);
 
        if (recompute) {
-               recompute_map (true);
+               solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
        }
 
 }
 
 void
-TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
+TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
 {
-       Tempo newtempo (beats_per_minute, note_type);
-       TempoSection* t;
 
-       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);
-                       }
-                       PropertyChanged (PropertyChange ());
-                       break;
-               }
+       MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
+
+       double pulse = pulse_at_frame_locked (_metrics, frame);
+       new_meter->set_pulse (pulse);
+
+       do_insert (new_meter);
+
+       if (recompute) {
+               solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
        }
+
 }
 
-void
-TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
+/**
+* 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)
 {
-       Tempo newtempo (beats_per_minute, note_type);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics future_map;
+       framepos_t ret = 0;
+       TempoSection* new_section = copy_metrics_and_point (future_map, section);
 
-       TempoSection* prev;
-       TempoSection* first;
-       Metrics::iterator i;
+       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);
+       }
 
-       /* find the TempoSection immediately preceding "where"
-        */
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       return ret;
+}
 
-       for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
+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 ((*i)->frame() > where) {
+       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, pulse_at_beat_locked (_metrics, 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
+}
+
+bool
+TempoMap::gui_change_tempo (TempoSection* ts,  const Tempo& bpm)
+{
+       Metrics future_map;
+       bool can_solve = false;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
+               new_section->set_beats_per_minute (bpm.beats_per_minute());
+               recompute_tempos (future_map);
+
+               if (check_solved (future_map, true)) {
+                       ts->set_beats_per_minute (bpm.beats_per_minute());
+                       recompute_map (_metrics);
+                       can_solve = true;
+               }
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       if (can_solve) {
+               MetricPositionChanged (); // Emit Signal
+       }
+       return can_solve;
+}
+
+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());
+                               }
+                               ret->set_active (t->active());
+                               ret->set_movable (t->movable());
+                               copy.push_back (ret);
+                               continue;
+                       }
+                       TempoSection* cp = 0;
+                       if (t->position_lock_style() == MusicTime) {
+                               cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
+                       } else {
+                               cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
+                       }
+                       cp->set_active (t->active());
+                       cp->set_movable (t->movable());
+                       copy.push_back (cp);
+               }
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       MeterSection* cp = 0;
+                       if (m->position_lock_style() == MusicTime) {
+                               cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
+                       } else {
+                               cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
+                       }
+                       cp->set_movable (m->movable());
+                       copy.push_back (cp);
+               }
+       }
+       //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, pulse_at_beat_locked (copy, beat));
+
+       Metrics::const_iterator d = copy.begin();
+       while (d != copy.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       return ret;
+}
+
+void
+TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
+{
+       Tempo newtempo (beats_per_minute, note_type);
+       TempoSection* t;
+
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       {
+                               Glib::Threads::RWLock::WriterLock lm (lock);
+                               *((Tempo*) t) = newtempo;
+                               recompute_map (_metrics);
+                       }
+                       PropertyChanged (PropertyChange ());
+                       break;
+               }
+       }
+}
+
+void
+TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
+{
+       Tempo newtempo (beats_per_minute, note_type);
+
+       TempoSection* prev;
+       TempoSection* first;
+       Metrics::iterator i;
+
+       /* find the TempoSection immediately preceding "where"
+        */
+
+       for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
+
+               if ((*i)->frame() > where) {
                        break;
                }
 
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
                        if (!first) {
                                first = t;
                        }
@@ -841,7 +1344,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 ());
@@ -852,13 +1355,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;
 }
@@ -870,7 +1373,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;
                }
@@ -888,9 +1391,14 @@ 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;
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               return *t;
+                       }
                }
        }
 
@@ -904,9 +1412,14 @@ 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;
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               return *t;
+                       }
                }
        }
 
@@ -914,102 +1427,95 @@ TempoMap::first_tempo ()
        abort(); /*NOTREACHED*/
        return *t;
 }
-
 void
-TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
+TempoMap::recompute_tempos (Metrics& metrics)
 {
-       /* 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
-                  the last metric.
-               */
-               end = max_framepos;
+       TempoSection* prev_ts = 0;
 
-       } else {
-               /*
-               if (!_map.empty ()) {
-                       /* never allow the map to be shortened /
-                       end = max (end, _map.back().frame);
-               }
-               */
-       }
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       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));
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* ms;
+                               } 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));
 
-               if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       meter = ms;
-                       break;
+                               }
+                       }
+                       prev_ts = t;
                }
        }
+       prev_ts->set_c_func (0.0);
+}
 
-       assert(meter);
+/* tempos must be positioned correctly */
+void
+TempoMap::recompute_meters (Metrics& metrics)
+{
+       MeterSection* meter = 0;
+       MeterSection* prev_m = 0;
+       uint32_t accumulated_bars = 0;
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* ts;
+       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_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
+                       }
+                       if (meter->position_lock_style() == AudioTime) {
+                               double pulse = 0.0;
+                               pair<double, BBT_Time> b_bbt;
+                               if (prev_m) {
+                                       double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
+                                       b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
+                                       const double true_pulse = prev_m->pulse() + (ceil (beats) - prev_m->beat()) / prev_m->note_divisor();
+                                       const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
+                                       pulse = true_pulse - pulse_off;
+                               }
+                               if (!meter->movable()) {
+                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                               }
+                               meter->set_beat (b_bbt);
+                               meter->set_pulse (pulse);
+                       } else {
+                               double pulse = 0.0;
+                               if (prev_m) {
+                                       pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
+                               } else {
+                                       pulse = pulse_at_beat_locked (metrics, meter->beat());
+                               }
+                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+                               meter->set_pulse (pulse);
+                       }
 
-               if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       tempo = ts;
-                       break;
+                       prev_m = meter;
                }
        }
+}
 
-       assert(tempo);
-
-       /* assumes that the first meter & tempo are at frame zero */
-       current_frame = 0;
-       meter->set_frame (0);
-       tempo->set_frame (0);
-
-       /* assumes that the first meter & tempo are at 1|1|0 */
-       current.bars = 1;
-       current.beats = 1;
-       current.ticks = 0;
-       if (reassign_tempo_bbt) {
-
-               MeterSection* rmeter = meter;
-
-               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
-
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-
-                       TempoSection* ts;
-                       MeterSection* ms;
-
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+void
+TempoMap::recompute_map (Metrics& metrics, framepos_t end)
+{
+       /* CALLER MUST HOLD WRITE LOCK */
 
-                               /* reassign the BBT time of this tempo section
-                                * based on its bar offset position.
-                                */
+       if (end < 0) {
 
-                               ts->update_bbt_time_from_bar_offset (*rmeter);
+               /* we will actually stop once we hit
+                  the last metric.
+               */
+               end = max_framepos;
 
-                       } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               rmeter = ms;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
-                       }
-               }
        }
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
-
-       next_metric = metrics.begin();
-       ++next_metric; // skip meter (or tempo)
-       ++next_metric; // skip tempo (or meter)
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
        if (end == 0) {
                /* silly call from Session::process() during startup
@@ -1017,85 +1523,66 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                return;
        }
 
-       _extend_map (tempo, meter, next_metric, current, current_frame, end);
+       recompute_tempos (metrics);
+       recompute_meters (metrics);
 }
 
-void
-TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
-                      Metrics::iterator next_metric,
-                      BBT_Time current, framepos_t current_frame, framepos_t end)
+double
+TempoMap::pulse_at_beat (const double& beat) const
 {
-       /* CALLER MUST HOLD WRITE LOCK */
-
-       uint32_t first_tick_in_new_meter = 0;
-       Metrics::const_iterator i;
-       Metrics::const_iterator mi;
-
-       TempoSection* prev_ts = tempo;
-
-       for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
-               MeterSection* m = 0;
-
-               if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
-
-                       if (m->start() >= prev_ts->start()) {
-                               first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter
-                               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                                       TempoSection* t;
-
-                                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                                               if (t->start() >= m->start() && t->start() > prev_ts->start()) {
-                                                       //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
-                                                       //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
-
-                                                       /*tempo section (t) lies in the previous meter */
-                                                       double ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) )  * BBT_Time::ticks_per_beat) + t->start().ticks;
-
-
-                                                       double ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1))  * BBT_Time::ticks_per_beat) + prev_ts->start().ticks;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return pulse_at_beat_locked (_metrics, beat);
+}
 
-                                                       double ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
-                                                       /* assume (falsely) that the target tempo is constant */
-                                                       double length_estimate = (ticks_relative_to_prev_ts /  BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
-                                                       if (prev_ts->type() == TempoSection::TempoSectionType::Constant) {
-                                                               cerr << "constant type " << endl;
-                                                               length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
-                                                       }
-                                                       cerr<< "initial length extimate = " << length_estimate << " ticks_relative_to_prev_ts " << ticks_relative_to_prev_ts << endl;
-                                                       double system_precision_at_target_tempo =  (_frame_rate / t->ticks_per_minute());
-                                                       cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
-                                                       double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
+double
+TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
 
-                                                       while (fabs (tick_error) >= system_precision_at_target_tempo) {
+       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 actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate);
-                                                               tick_error = ticks_relative_to_prev_ts - actual_ticks;
-                                                               length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
-                                                               //cerr << "actual ticks = " << actual_ticks << endl;
+       }
+       double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
+       return ret;
+}
 
-                                                               //cerr << "tick error  = " << tick_error << endl;
-                                                       }
-                                                       cerr << "setting t frame to " << length_estimate + prev_ts->frame() << "tick error  = " << tick_error << endl;
-                                                       t->set_frame (length_estimate + prev_ts->frame());
+double
+TempoMap::beat_at_pulse (const double& pulse) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_pulse_locked (_metrics, pulse);
+}
 
-                                                       if (m->start() < t->start() && m->start() == prev_ts->start()) {
-                                                               m->set_frame (prev_ts->frame());
-                                                       } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
-                                                               cerr << "setting m frame to " << prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) << " ticks = " << first_tick_in_new_meter - ticks_at_prev_ts  << endl;
+double
+TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
 
-                                                               m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate));
-                                                       }
-                                               }
-                                               prev_ts = t;
-                                       }
-                               }
+       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;
                        }
-                       meter = m;
+                       accumulated_beats = m->beat();
+                       prev_ms = m;
                }
        }
-}
 
+       double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
+
+       return beats_in_section + accumulated_beats;
+}
 
 TempoMetric
 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
@@ -1110,7 +1597,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
           now see if we can find better candidates.
        */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
                if ((*i)->frame() > frame) {
                        break;
@@ -1126,6 +1613,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
        return m;
 }
 
+/* XX meters only */
 TempoMetric
 TempoMap::metric_at (BBT_Time bbt) const
 {
@@ -1139,15 +1627,17 @@ TempoMap::metric_at (BBT_Time bbt) const
           now see if we can find better candidates.
        */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       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());
 
-               BBT_Time section_start ((*i)->start());
+                       if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+                               break;
+                       }
 
-               if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
-                       break;
+                       m.set_metric (*i);
                }
-
-               m.set_metric (*i);
        }
 
        return m;
@@ -1156,7 +1646,6 @@ TempoMap::metric_at (BBT_Time bbt) const
 void
 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
 
        if (frame < 0) {
                bbt.bars = 1;
@@ -1165,161 +1654,171 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
                warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
                return;
        }
-       bbt = beats_to_bbt (beat_at_frame (frame));
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       double const beat = beat_at_frame_locked (_metrics, frame);
+
+       bbt = beats_to_bbt_locked (_metrics, beat);
 }
 
-int32_t
-TempoMap::bars_in_meter_section (MeterSection* ms) const
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
 {
-       /* YOU MUST HAVE THE READ LOCK */
-       Metrics::const_iterator i;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       MeterSection* next_ms = 0;
-       const MeterSection* prev_ms = &first_meter();
+       return bbt_to_beats_locked (_metrics, bbt);
+}
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+double
+TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
+{
+       /* CALLER HOLDS READ LOCK */
+
+       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) {
-                       if (ms->frame() < m->frame()) {
-                               next_ms = m;
-                               break;
+                       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;
                }
        }
-       if (next_ms) {
-               double ticks_at_next = tick_at_frame (next_ms->frame());
-               double ticks_at_prev = tick_at_frame (prev_ms->frame());
-               double ticks_in_meter = ticks_at_next - ticks_at_prev;
 
-               return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
-       }
-       return -1;
+       double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
+       double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
+       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+
+       return ret;
 }
 
 Timecode::BBT_Time
-TempoMap::beats_to_bbt (double beats)
+TempoMap::beats_to_bbt (const double& beats)
 {
-       /* CALLER HOLDS READ LOCK */
-       BBT_Time ret;
-       MeterSection* prev_ms = &first_meter();
-
-       framecnt_t frame = frame_at_beat (beats);
-       uint32_t cnt = 0;
-
-       if (n_meters() < 2) {
-               uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
-               double remaining_beats = beats - (bars *  prev_ms->note_divisor());
-               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
-
-               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
-               ret.beats = (uint32_t) floor (remaining_beats);
-               ret.bars = bars;
-
-               /* 0 0 0 to 1 1 0 - based mapping*/
-               ++ret.bars;
-               ++ret.beats;
-
-               if (ret.ticks >= BBT_Time::ticks_per_beat) {
-                       ++ret.beats;
-                       ret.ticks -= BBT_Time::ticks_per_beat;
-               }
-
-               if (ret.beats > prev_ms->note_divisor()) {
-                       ++ret.bars;
-                       ret.beats = 1;
-               }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-               return ret;
-       }
+       return beats_to_bbt_locked (_metrics, beats);
+}
 
-       uint32_t first_beat_in_meter = 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;
-       Metrics::const_iterator i;
+       double accumulated_beats = 0.0;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m = 0;
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       first_beat_in_meter = beat_at_frame (m->frame());
 
-                       if (beats < first_beat_in_meter) {
-                               /* this is the meter after the one our beat is on*/
-                               break;
-                       }
-                       int32_t const bars_in_ms = bars_in_meter_section (m);
+                       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;
+                               }
 
-                       if (bars_in_ms > 0) {
-                               accumulated_bars += bars_in_ms;
+                               /* we need a whole number of bars. */
+                               accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
+                               accumulated_beats += beats_to_m;
                        }
 
                        prev_ms = m;
-                       ++cnt;
                }
        }
-       //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter =  " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars <<  endl;
-
-       if (beats > first_beat_in_meter) {
-               /* prev_ms is the relevant one here */
 
-               /* now get the ticks at frame */
-               double ticks_at_frame = tick_at_frame (frame);
+       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;
 
-               /* find the number of ticks at the beginning of the meter section (bar 1)*/
-               double ticks_at_ms = tick_at_frame (prev_ms->frame());
+       BBT_Time ret;
 
-               double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-               uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
-               double remaining_beats = beats_used_by_ms - (bars *  prev_ms->note_divisor());
-               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
+       /* 0 0 0 to 1 1 0 - based mapping*/
+       ++ret.bars;
+       ++ret.beats;
 
-               ret.bars = bars + accumulated_bars;
-               ret.beats = (uint32_t) floor (remaining_beats);
-               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
+       }
 
-               /* now ensure we srtart at 1 1 0 */
+       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
                ++ret.bars;
-               ++ret.beats;
-               //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
-               if (ret.ticks >= BBT_Time::ticks_per_beat) {
-                       ++ret.beats;
-                       ret.ticks -= BBT_Time::ticks_per_beat;
-               }
+               ret.beats = 1;
+       }
 
-               if (ret.beats > prev_ms->note_divisor()) {
-                       ++ret.bars;
-                       ret.beats = 1;
-               }
+       return ret;
+}
 
-               return ret;
-       }
+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;
 
-       /* find the number of ticks at the beginning of the meter section (bar 1)*/
-       double ticks_at_ms = tick_at_frame (prev_ms->frame());
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-       /* now get the ticks at frame */
-       double ticks_at_frame = tick_at_frame (frame);
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-       double ticks_within_ms = ticks_at_frame - ticks_at_ms;
+                       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;
+                               }
 
-       ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
-       uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
-       ret.beats = (uint32_t) floor (remaining_ticks);
-       remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
+                               /* 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();
+                       }
+
+                       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;
+
+       BBT_Time ret;
 
-       /* only round ticks */
        ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-       /* now ensure we srtart at 1 1 0 */
+       /* 0 0 0 to 1 1 0 mapping*/
        ++ret.bars;
        ++ret.beats;
+
        if (ret.ticks >= BBT_Time::ticks_per_beat) {
                ++ret.beats;
                ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       if (ret.beats > prev_ms->note_divisor()) {
+       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
                ++ret.bars;
                ret.beats = 1;
        }
@@ -1328,103 +1827,143 @@ TempoMap::beats_to_bbt (double beats)
 }
 
 double
-TempoMap::tick_at_frame (framecnt_t frame) const
+TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_frame_locked (_metrics, frame);
+}
 
-       Metrics::const_iterator i;
-       const TempoSection* prev_ts = &first_tempo();
-       double accumulated_ticks = 0.0;
-       uint32_t cnt = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+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, frame);
 
-                       if (frame < t->frame()) {
-                               /*the previous ts is the one containing the frame */
+       return beat_at_pulse_locked (metrics, pulse);
+}
 
-                               framepos_t time = frame - prev_ts->frame();
-                               framepos_t last_frame = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
+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;
 
-                               return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
                        }
-
-                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
-                               framepos_t time = t->frame() - prev_ts->frame();
-                               framepos_t last_frame = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
-                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
+                       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;
-                       ++cnt;
                }
        }
 
-       /* treated s linear for this ts */
-       framecnt_t frames_in_section = frame - prev_ts->frame();
-       double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
-
-       return ticks_in_section + accumulated_ticks;
+       /* treated as constant for this ts */
+       double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
 
+       return pulses_in_section + accumulated_pulses;
 }
 
 framecnt_t
-TempoMap::frame_at_tick (double tick) const
+TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return frame_at_beat_locked (_metrics, beat);
+}
 
-       double accumulated_ticks = 0.0;
-       const TempoSection* prev_ts =  &first_tempo();
-       uint32_t cnt = 0;
+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;
+}
 
-       Metrics::const_iterator i;
+framecnt_t
+TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       /* HOLD THE READER LOCK */
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       const TempoSection* prev_ts = 0;
+       double accumulated_pulses = 0.0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
-                               framepos_t time = t->frame() - prev_ts->frame();
-                               framepos_t last_time = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
-                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
                        }
-
-                       if (tick < accumulated_ticks) {
-                               /* prev_ts is the one affecting us. */
-
-                               double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
-                               framepos_t section_start = prev_ts->frame();
-                               framepos_t last_time = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
-                               return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
+                       if (prev_ts && t->pulse() > pulse) {
+                               return prev_ts->frame_at_pulse (pulse, _frame_rate);
                        }
 
+                       accumulated_pulses = t->pulse();
                        prev_ts = t;
-                       ++cnt;
                }
        }
-       double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
-       double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
-       framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+       /* 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);
+
+       framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
 
        return ret;
 }
 
 double
-TempoMap::beat_at_frame (framecnt_t frame) const
+TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
 {
-       return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
+       MeterSection* prev_m = 0;
+       double beat_off = first_meter().pulse();
+
+       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->beat() > beat) {
+                                       break;
+                               }
+
+                               if (m->position_lock_style() == AudioTime) {
+                                       beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
+                               }
+                       }
+                       prev_m = m;
+               }
+       }
+
+       return beat_off;
 }
 
-framecnt_t
-TempoMap::frame_at_beat (double beat) const
+frameoffset_t
+TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
 {
-       return frame_at_tick (beat * BBT_Time::ticks_per_beat);
+       frameoffset_t frame_off = 0;
+       MeterSection* prev_m = 0;
+
+       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 (prev_m && m->position_lock_style() == AudioTime) {
+                               const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
+                               frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
+                       }
+                       prev_m = m;
+               }
+       }
+
+       return frame_off;
 }
 
 framepos_t
@@ -1438,113 +1977,417 @@ TempoMap::frame_time (const BBT_Time& bbt)
        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;
+}
+
+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 (!t->active()) {
+                               continue;
+                       }
+                       if (prev_ts) {
+                               if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
+                                       return false;
+                               }
+
+                               if (t->frame() == prev_ts->frame()) {
+                                       return false;
+                               }
+
+                               /* precision check ensures pulses and frames align independent of lock style.*/
+                               if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
+                                       return false;
+                               }
+                       }
+                       prev_ts = t;
+               }
+       }
+
+       return true;
+}
+
+bool
+TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
+{
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->movable()) {
+                               t->set_active (true);
+                               continue;
+                       }
+                       if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
+                               t->set_active (false);
+                               t->set_pulse (0.0);
+                       } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
+                               t->set_active (true);
+                       } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
+
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
+{
+       TempoSection* prev_ts = 0;
+       TempoSection* section_prev = 0;
+       framepos_t first_m_frame = 0;
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (!m->movable()) {
+                               first_m_frame = m->frame();
+                               break;
+                       }
+               }
+       }
+       if (section->movable() && frame <= first_m_frame) {
+               return false;
+       } else {
+               section->set_active (true);
+       }
+       section->set_frame (frame);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if (!t->active()) {
+                               continue;
+                       }
+                       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_frame (section->pulses_per_minute(), frame, _frame_rate));
+               section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
+       }
+
+       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;
+       }
+
+       MetricSectionFrameSorter fcmp;
+       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;
+       }
+
+       MetricSectionSorter cmp;
+       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 {
+               recompute_tempos (imaginary);
+       }
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+       //dump (imaginary, std::cerr);
+
+       return false;
+}
+
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
+{
+       TempoSection* prev_ts = 0;
+       TempoSection* section_prev = 0;
+
+       section->set_pulse (pulse);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       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));
+       }
+
+       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;
+       }
+
+       MetricSectionSorter cmp;
+       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);
+       }
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+
+       MetricSectionFrameSorter fcmp;
+       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 {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+
+       //dump (imaginary, std::cerr);
+
+       return false;
+}
+
+void
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
+{
+       MeterSection* prev_ms = 0;
+
+       if (!section->movable()) {
+               /* lock the first tempo to our first meter */
+               if (!set_active_tempos (imaginary, frame)) {
+                       return;
+               }
+               TempoSection* first_t = &first_tempo();
+               Metrics future_map;
+               TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
+
+               new_section->set_frame (frame);
+               new_section->set_pulse (0.0);
+               new_section->set_active (true);
+
+               if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
+                       first_t->set_frame (frame);
+                       first_t->set_pulse (0.0);
+                       first_t->set_active (true);
+                       solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
+               } else {
+                       return;
+               }
+       }
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       Metrics::const_iterator i;
        uint32_t accumulated_bars = 0;
 
-       MeterSection* prev_ms = &first_meter();
+       section->set_frame (frame);
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       int32_t const bims = bars_in_meter_section (m);
-
-                       if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
-                               break;
+                       if (prev_ms) {
+                               const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
+                               accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
+                       }
+                       if (m == section){
+                               /*
+                                 here we set the beat for this frame.
+                                 we're going to set it 'incorrectly' to the next integer and use this difference
+                                 to find the meter's pulse later.
+                                 (meters should fall on absolute beats to keep us sane)
+                               */
+                               double pulse = 0.0;
+                               pair<double, BBT_Time> b_bbt;
+                               if (m->movable()) {
+                                       double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
+                                       b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
+                                       const double true_pulse = prev_ms->pulse() + ((ceil (beats) - prev_ms->beat()) / prev_ms->note_divisor());
+                                       const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
+                                       pulse = true_pulse - pulse_off;
+                               } else {
+                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                               }
+                               m->set_beat (b_bbt);
+                               m->set_pulse (pulse);
+                               prev_ms = m;
+                               continue;
                        }
-                       if (bims > 0 ) {
-                               accumulated_bars += bims;
+                       if (prev_ms) {
+                               if (m->position_lock_style() == MusicTime) {
+                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
+                                       m->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                                       m->set_pulse (pulse);
+                               } else {
+                                       if (!m->movable()) {
+                                               pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                                               m->set_beat (b_bbt);
+                                       }
+                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
+                                       m->set_pulse (pulse);
+                               }
                        }
                        prev_ms = m;
                }
        }
 
-       uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
-       double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
-       double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
-       double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
+       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 {
+               recompute_meters (imaginary);
+       }
+       //dump (imaginary, std::cerr);
+}
 
-       TempoSection* prev_ts = &first_tempo();
-       double accumulated_ticks = 0.0;
-       uint32_t cnt = 0;
+void
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
+{
+       MeterSection* prev_ms = 0;
+       double accumulated_beats = 0.0;
+       uint32_t accumulated_bars = 0;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (t->frame() < prev_ms->frame()) {
-                               continue;
-                       }
+       section->set_pulse (pulse);
 
-                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
-                               /*find the number of ticke in this section */
-                               framepos_t const time = t->frame() - prev_ts->frame();
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
-                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+       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 (ticks_target < accumulated_ticks) {
-                               double const ticks_in_section = ticks_target - tick_at_frame (prev_ts->frame());
-                               framepos_t const section_start_time = prev_ts->frame();
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
-                               framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
-                               return ret;
+                       if (m == section){
+                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                               pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
+                               section->set_beat (b_bbt);
+                               prev_ms = section;
+                               continue;
                        }
-
-                       prev_ts = t;
-                       ++cnt;
+                       if (prev_ms) {
+                               if (m->position_lock_style() == MusicTime) {
+                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
+                                       m->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                                       m->set_pulse (pulse);
+                               } else {
+                                       if (!m->movable()) {
+                                               pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                                               m->set_beat (b_bbt);
+                                       }
+                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
+                                       m->set_pulse (pulse);
+                               }
+                       }
+                       prev_ms = m;
                }
        }
 
-       /*treat this ts as constant tempo */
-       double const ticks_in_this_ts = ticks_target - tick_at_frame (prev_ts->frame());
-       double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
-       framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
-       return ret;
+       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);
+       }
 }
 
-
 framecnt_t
 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       Metrics::const_iterator i;
-       TempoSection* first = 0;
-       TempoSection* second = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                       if ((*i)->frame() > pos) {
-                               second = t;
-                               break;
-                       }
-
-                       first = t;
-               }
-       }
-       if (first && second) {
-               framepos_t const last_time = second->frame() - first->frame();
-               double const last_beats_per_minute = second->beats_per_minute();
-
-               framepos_t const time = pos - first->frame();
-               double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
-               double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-
-               double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
+       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 time_at_bbt - time;
-       }
-
-       double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-       return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
+       return ret;
 }
 
 framepos_t
@@ -1563,10 +2406,9 @@ framepos_t
 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
+       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;
+       uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
 
        ticks -= beats * BBT_Time::ticks_per_beat;
 
@@ -1650,7 +2492,61 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        /* on the subdivision, do nothing */
                }
        }
-       return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
+
+       framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+
+       return ret_frame;
+}
+
+void
+TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
+{
+       if (sub_num == -1) {
+               const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+               if ((double) when.beats > bpb / 2.0) {
+                       ++when.bars;
+               }
+               when.beats = 1;
+               when.ticks = 0;
+               return;
+       } else if (sub_num == 0) {
+               const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+               if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
+                       ++when.beats;
+                       while ((double) when.beats > bpb) {
+                               ++when.bars;
+                               when.beats -= (uint32_t) floor (bpb);
+                       }
+               }
+               when.ticks = 0;
+               return;
+       }
+       const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
+       double rem;
+       if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
+               /* closer to the next subdivision, so shift forward */
+
+               when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
+
+               if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
+                       ++when.beats;
+                       when.ticks -= Timecode::BBT_Time::ticks_per_beat;
+               }
+
+       } else if (rem > 0) {
+               /* closer to previous subdivision, so shift backward */
+
+               if (rem > when.ticks) {
+                       if (when.beats == 0) {
+                               /* can't go backwards past zero, so ... */
+                       }
+                       /* step back to previous beat */
+                       --when.beats;
+                       when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
+               } else {
+                       when.ticks = when.ticks - rem;
+               }
+       }
 }
 
 framepos_t
@@ -1658,9 +2554,8 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       double const beat_at_framepos = beat_at_frame (frame);
-
-       BBT_Time bbt (beats_to_bbt (beat_at_framepos));
+       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:
@@ -1696,11 +2591,11 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 
        case Beat:
                if (dir < 0) {
-                       return frame_at_beat (floor (beat_at_framepos));
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
                } else if (dir > 0) {
-                       return frame_at_beat (ceil (beat_at_framepos));
+                       return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
                } else {
-                       return frame_at_beat (floor (beat_at_framepos + 0.5));
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
                }
                break;
        }
@@ -1713,58 +2608,44 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    framepos_t lower, framepos_t upper)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
-       uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
-
+       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 const pos = frame_at_beat (cnt);
-               MeterSection const meter = meter_section_at (pos);
-               Tempo const tempo = tempo_at (pos);
-               BBT_Time const bbt = beats_to_bbt ((double) cnt);
-
-               points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
+               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);
+               BBTPoint point = BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func());
+               points.push_back (point);
                ++cnt;
        }
 }
 
-TempoSection*
-TempoMap::tempo_section_after (framepos_t frame) const
+const TempoSection&
+TempoMap::tempo_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       Metrics::const_iterator i;
-       TempoSection* next = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                       if ((*i)->frame() > frame) {
-                               next = t;
-                               break;
-                       }
-               }
-       }
-
-       return next;
+       return tempo_section_at_locked (frame);
 }
 
-
 const TempoSection&
-TempoMap::tempo_section_at (framepos_t frame) const
+TempoMap::tempo_section_at_locked (framepos_t frame) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
        Metrics::const_iterator i;
        TempoSection* prev = 0;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                       if ((*i)->frame() > frame) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (t->frame() > frame) {
                                break;
                        }
 
@@ -1780,71 +2661,100 @@ TempoMap::tempo_section_at (framepos_t frame) const
        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 (framepos_t frame, framecnt_t sr) const
+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 (frame);
-       const TempoSection* ts_after = tempo_section_after (frame);
+       const TempoSection* ts_at = &tempo_section_at_locked (frame);
+       const TempoSection* ts_after = 0;
+       Metrics::const_iterator i;
+
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if ((*i)->frame() > frame) {
+                               ts_after = t;
+                               break;
+                       }
+               }
+       }
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
        }
        /* must be treated as constant tempo */
        return ts_at->frames_per_beat (_frame_rate);
 }
 
 const Tempo
-TempoMap::tempo_at (framepos_t frame) const
+TempoMap::tempo_at (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return tempo_at_locked (frame);
+}
 
-       TempoMetric m (metric_at (frame));
+const Tempo
+TempoMap::tempo_at_locked (const framepos_t& frame) const
+{
        TempoSection* prev_ts = 0;
 
        Metrics::const_iterator i;
 
-       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 (!t->active()) {
+                               continue;
+                       }
                        if ((prev_ts) && t->frame() > frame) {
-                               /* this is the one past frame */
-                               framepos_t const time = frame - prev_ts->frame();
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
-                               double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
-                               Tempo const ret_tempo (ret, m.tempo().note_type ());
+                               /* 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;
                }
        }
 
-       return m.tempo();
+       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) {
-               MeterSection* t;
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+               MeterSection* m;
 
-               if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                       if ((*i)->frame() > frame) {
+                       if (prev && (*i)->frame() > frame) {
                                break;
                        }
 
-                       prev = t;
+                       prev = m;
                }
        }
 
@@ -1863,6 +2773,25 @@ TempoMap::meter_at (framepos_t frame) const
        return m.meter();
 }
 
+const MeterSection&
+TempoMap::meter_section_at (const double& beat) const
+{
+       MeterSection* prev_ms = 0;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       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;
+                       }
+                       prev_ms = m;
+               }
+
+       }
+       return *prev_ms;
+}
+
 XMLNode&
 TempoMap::get_state ()
 {
@@ -1871,7 +2800,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());
                }
        }
@@ -1887,9 +2816,8 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                XMLNodeList nlist;
                XMLNodeConstIterator niter;
-               Metrics old_metrics (metrics);
-               MeterSection* last_meter = 0;
-               metrics.clear();
+               Metrics old_metrics (_metrics);
+               _metrics.clear();
 
                nlist = node.children();
 
@@ -1900,18 +2828,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        TempoSection* ts = new TempoSection (*child);
-                                       metrics.push_back (ts);
-
-                                       if (ts->bar_offset() < 0.0) {
-                                               if (last_meter) {
-                                                       ts->update_bar_offset_from_bbt (*last_meter);
-                                               }
-                                       }
+                                       _metrics.push_back (ts);
                                }
 
                                catch (failed_constructor& err){
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
 
@@ -1919,13 +2841,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        MeterSection* ms = new MeterSection (*child);
-                                       metrics.push_back (ms);
-                                       last_meter = ms;
+                                       _metrics.push_back (ms);
                                }
 
                                catch (failed_constructor& err) {
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
                        }
@@ -1933,26 +2854,55 @@ 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* m;
+                       TempoSection* t;
+                       MeterSection* prev_ms = 0;
+                       TempoSection* prev_ts = 0;
+                       if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               if (prev_ms && prev_ms->pulse() < 0.0) {
+                                       /*XX we cannot possibly make this work??. */
+                                       pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
+                                       prev_ms->set_beat (start);
+                                       const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
+                                       prev_ms->set_pulse (start_pulse);
+                               }
+                               prev_ms = m;
+                       } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               if (!t->active()) {
+                                       continue;
+                               }
+                               if (prev_ts && prev_ts->pulse() < 0.0) {
+                                       double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
+                                       prev_ts->set_pulse (start);
+                               }
+                               prev_ts = t;
+                       }
                }
-
                /* 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;
                                        }
                                }
@@ -1960,7 +2910,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        prev = i;
                }
 
-               recompute_map (true, -1);
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -1969,22 +2919,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 << 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
@@ -1993,7 +2951,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++;
                }
@@ -2008,7 +2966,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++;
                }
@@ -2022,7 +2980,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);
                        }
@@ -2050,51 +3008,85 @@ 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) {
+                                               if (!t->active()) {
+                                                       continue;
+                                               }
+                                               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->beat(), m->bbt());
+                                               ms->set_beat (start);
+                                               ms->set_pulse (m->pulse());
+                                       }
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               if (!t->active()) {
+                                                       continue;
+                                               }
+                                               const double beat = beat_at_pulse_locked (_metrics, t->pulse());
+                                               pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
+                                               ms->set_beat (start);
+                                               ms->set_pulse (t->pulse());
+                                       }
+                                       ms->set_frame (prev->frame());
+                               }
+
                        } else {
                                // metric will be at frames=0 bbt=1|1|0 by default
                                // which is correct for our purpose
                        }
 
-                       bbt_time ((*i)->frame(), bbt);
-
-                       // 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) {
+                               if (!t->active()) {
+                                       continue;
                                }
+                               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 (beat_at_frame_locked (_metrics, m->frame()), bbt);
+                               m->set_beat (start);
+                               m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
                                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*/
@@ -2103,7 +3095,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        prev = (*i);
                }
 
-               recompute_map (true);
+               recompute_map (_metrics);
        }
 
 
@@ -2122,7 +3114,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);
@@ -2158,12 +3150,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 ());
@@ -2190,169 +3182,25 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
-       cerr << "framepos_plus_bbt - untested" << endl;
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       Metrics::const_iterator i;
-       const MeterSection* meter;
-       const MeterSection* m;
-       const TempoSection* tempo;
-       const TempoSection* next_tempo = 0;
-       const TempoSection* t;
-       double frames_per_beat;
-       framepos_t effective_pos = max (pos, (framepos_t) 0);
-
-       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;
-               }
-       }
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               if ((*i)->frame() > effective_pos) {
-                       if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                               next_tempo = t;
-                       }
-                       break;
-               }
-       }
-
-       /* We now have:
-
-          meter -> the Meter for "pos"
-          tempo -> the Tempo for "pos"
-          next_tempo -> the Tempo after "pos" or 0
-          i     -> for first new metric after "pos", possibly metrics.end()
-       */
-
-       /* now comes the complicated part. we have to add one beat a time,
-          checking for a new metric on every beat.
-       */
-
-       uint64_t bars = 0;
-       /* fpb is used for constant tempo */
-       frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
-       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.
-                                */
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       next_tempo = t;
-                               }
-
-                               if (next_tempo) {
-                                       pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
-                               } else {
-                                       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);
-                       }
-               }
-
-       }
-
-       if (next_tempo) {
-               pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
-       } else {
-               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.
-                                */
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       next_tempo = t;
-                               }
-
-                               if (next_tempo) {
-                                       pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
-                               } else {
-                                       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);
-                       }
-               }
-       }
-
-       if (next_tempo) {
-               pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
-       } else {
-               pos += llrint (beats * frames_per_beat);
+       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;
        }
-
-       if (op.ticks) {
-               pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+       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,
@@ -2361,7 +3209,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
 {
-       return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
+       return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
 }
 
 struct bbtcmp {
@@ -2383,7 +3231,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;