Tempo ramps - consolidate meter dragging code.
[ardour.git] / libs / ardour / tempo.cc
index ffdf2bcf0f05e9249be9e9c3e75575e338c9a535..4a0151b22c7298df3c60b3ee446a656c638d5db3 100644 (file)
@@ -73,17 +73,20 @@ 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 (0.0)
+       : MetricSection (0.0, 0, MusicTime)
        , Tempo (TempoMap::default_tempo())
        , _c_func (0.0)
        , _active (true)
+       , _locked_to_meter (false)
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
        LocaleGuard lg;
        BBT_Time bbt;
        double pulse;
        uint32_t frame;
 
+       _legacy_bbt = BBT_Time (0, 0, 0);
+
        if ((prop = node.property ("start")) != 0) {
                if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                            &bbt.bars,
@@ -92,20 +95,18 @@ TempoSection::TempoSection (const XMLNode& node)
                        /* legacy session - start used to be in bbt*/
                        _legacy_bbt = bbt;
                        pulse = -1.0;
-                       set_pulse (pulse);
+                       info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
                }
-       } else {
-               warning << _("TempoSection XML node has no \"start\" property") << endmsg;
        }
 
-
        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 (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
+                       error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
                }
        }
+
+       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;
@@ -155,10 +156,20 @@ TempoSection::TempoSection (const XMLNode& node)
        }
 
        if ((prop = node.property ("lock-style")) == 0) {
-               set_position_lock_style (MusicTime);
+               if (movable()) {
+                       set_position_lock_style (MusicTime);
+               } else {
+                       set_position_lock_style (AudioTime);
+               }
        } else {
                set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
        }
+
+       if ((prop = node.property ("locked-to-meter")) == 0) {
+               set_locked_to_meter (false);
+       } else {
+               set_locked_to_meter (string_is_affirmative (prop->value()));
+       }
 }
 
 XMLNode&
@@ -182,6 +193,7 @@ TempoSection::get_state() const
        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()));
+       root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
 
        return *root;
 }
@@ -198,7 +210,7 @@ double
 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
 
-       if (_type == Constant) {
+       if (_type == Constant || _c_func == 0.0) {
                return pulses_per_minute();
        }
 
@@ -213,7 +225,7 @@ TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate)
 framepos_t
 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
+       if (_type == Constant || _c_func == 0.0) {
                return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
        }
 
@@ -225,7 +237,7 @@ double
 TempoSection::tempo_at_pulse (const double& p) const
 {
 
-       if (_type == Constant) {
+       if (_type == Constant || _c_func == 0.0) {
                return pulses_per_minute();
        }
        double const ppm = pulse_tempo_at_pulse (p - pulse());
@@ -239,11 +251,10 @@ TempoSection::tempo_at_pulse (const double& p) const
 double
 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
-               double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
-               return  beats;
+       if (_type == Constant || _c_func == 0.0) {
+               double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+               return  pulses;
        }
-
        return pulse_at_pulse_tempo (ppm) + pulse();
 }
 
@@ -254,7 +265,7 @@ TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const fram
 double
 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
+       if (_type == Constant || _c_func == 0.0) {
                return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
        }
 
@@ -269,7 +280,7 @@ TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate)
 framepos_t
 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
+       if (_type == Constant || _c_func == 0.0) {
                return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
        }
 
@@ -305,7 +316,7 @@ The integral over t of our Tempo function (the beat function, which is the durat
 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
+t(b) = log((c.b / T0) + 1) / c
 
 The time t at which Tempo T occurs is a as above:
 t(T) = log(T / T0) / c
@@ -325,6 +336,13 @@ 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
 
+Of course the word 'beat' has been left loosely defined above.
+In music, a beat is defined by the musical pulse (which comes from the tempo)
+and the meter in use at a particular time (how many  pulse divisions there are in one bar).
+It would be more accurate to substitute the work 'pulse' for 'beat' above.
+
+Anyway ...
+
 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.
@@ -362,30 +380,30 @@ TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end
        return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
 }
 
-framecnt_t
+framepos_t
 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
 {
-       return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
+       return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
 }
 
 double
-TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
+TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
 {
        return (frame / (double) frame_rate) / 60.0;
 }
 
 /* position function */
 double
-TempoSection::a_func (double end_bpm, double c_func) const
+TempoSection::a_func (double end_ppm, double c_func) const
 {
-       return log (end_bpm / pulses_per_minute()) /  c_func;
+       return log (end_ppm / pulses_per_minute()) /  c_func;
 }
 
 /*function constant*/
 double
-TempoSection::c_func (double end_bpm, double end_time) const
+TempoSection::c_func (double end_ppm, double end_time) const
 {
-       return log (end_bpm / pulses_per_minute()) /  end_time;
+       return log (end_ppm / pulses_per_minute()) /  end_time;
 }
 
 /* tempo in ppm at time in minutes */
@@ -435,12 +453,10 @@ TempoSection::time_at_pulse (const double& pulse) const
 const string MeterSection::xml_state_node_name = "Meter";
 
 MeterSection::MeterSection (const XMLNode& node)
-       : MetricSection (0.0), Meter (TempoMap::default_meter())
+       : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter())
 {
        XMLProperty const * prop;
-       BBT_Time start;
        LocaleGuard lg;
-       const XMLProperty *prop;
        BBT_Time bbt;
        double pulse = 0.0;
        double beat = 0.0;
@@ -455,14 +471,13 @@ MeterSection::MeterSection (const XMLNode& node)
                        error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
                } else {
                        /* legacy session - start used to be in bbt*/
+                       info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
                        pulse = -1.0;
                }
-       } else {
-               error << _("MeterSection XML node has no \"start\" property") << endmsg;
        }
 
        if ((prop = node.property ("pulse")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
+               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
                        error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
                }
        }
@@ -470,20 +485,20 @@ MeterSection::MeterSection (const XMLNode& node)
 
        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;
+                       error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
                }
        }
 
        start.first = beat;
 
        if ((prop = node.property ("bbt")) == 0) {
-               error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
+               warning << _("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();
+               throw failed_constructor();
        }
 
        start.second = bbt;
@@ -519,19 +534,23 @@ MeterSection::MeterSection (const XMLNode& node)
                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();
        }
 
        set_movable (string_is_affirmative (prop->value()));
+
+       if ((prop = node.property ("lock-style")) == 0) {
+               warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
+               if (movable()) {
+                       set_position_lock_style (MusicTime);
+               } else {
+                       set_position_lock_style (AudioTime);
+               }
+       } else {
+               set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
+       }
 }
 
 XMLNode&
@@ -567,10 +586,9 @@ MeterSection::get_state() const
 /*
   Tempo Map Overview
 
-  Tempo can be thought of as a source of the musical pulse.
-  Meters divide that pulse into measures and beats.
-  Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
-  at any particular time.
+  Tempo is the rate of the musical pulse.
+  Meters divide the pulses into measures and beats.
+
   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.
 
@@ -583,6 +601,19 @@ MeterSection::get_state() const
 
   Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
 
+  With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
+  referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
+  sb->beat() - meter->beat() / meter->note_divisor().
+  Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
+  two meters is of course
+  (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
+
+  Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
+  Beat to frame conversion of course requires the use of meter and tempo.
+
+  Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
+  Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
+
   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.
@@ -618,8 +649,8 @@ TempoMap::TempoMap (framecnt_t fr)
        _frame_rate = fr;
        BBT_Time start (1, 1, 0);
 
-       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());
+       TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant, AudioTime);
+       MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
 
        t->set_movable (false);
        m->set_movable (false);
@@ -633,6 +664,12 @@ TempoMap::TempoMap (framecnt_t fr)
 
 TempoMap::~TempoMap ()
 {
+       Metrics::const_iterator d = _metrics.begin();
+       while (d != _metrics.end()) {
+               delete (*d);
+               ++d;
+       }
+       _metrics.clear();
 }
 
 void
@@ -663,6 +700,7 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
                if (dynamic_cast<TempoSection*> (*i) != 0) {
                        if (tempo.frame() == (*i)->frame()) {
                                if ((*i)->movable()) {
+                                       delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                }
@@ -693,14 +731,28 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 }
 
 bool
-TempoMap::remove_meter_locked (const MeterSection& tempo)
+TempoMap::remove_meter_locked (const MeterSection& meter)
 {
        Metrics::iterator i;
 
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+               TempoSection* t = 0;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (meter.frame() == (*i)->frame()) {
+                               if (t->locked_to_meter()) {
+                                       delete (*i);
+                                       _metrics.erase (i);
+                                       break;
+                               }
+                       }
+               }
+       }
+
        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<MeterSection*> (*i) != 0) {
-                       if (tempo.frame() == (*i)->frame()) {
+                       if (meter.frame() == (*i)->frame()) {
                                if ((*i)->movable()) {
+                                       delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                }
@@ -720,7 +772,6 @@ TempoMap::do_insert (MetricSection* section)
         */
        MeterSection* m = 0;
        if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
-               //assert (m->bbt().ticks == 0);
 
                if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
@@ -763,8 +814,13 @@ TempoMap::do_insert (MetricSection* section)
 
                                        *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        (*i)->set_position_lock_style (AudioTime);
+                                       TempoSection* t;
+                                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                                               t->set_type (insert_tempo->type());
+                                       }
                                        need_add = false;
                                } else {
+                                       delete (*i);
                                        _metrics.erase (i);
                                }
                                break;
@@ -776,7 +832,7 @@ TempoMap::do_insert (MetricSection* section)
 
                        bool const ipm = insert_meter->position_lock_style() == MusicTime;
 
-                       if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
+                       if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
 
                                if (!meter->movable()) {
 
@@ -786,11 +842,11 @@ TempoMap::do_insert (MetricSection* section)
                                         */
 
                                        *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
-                                       (*i)->set_position_lock_style (insert_meter->position_lock_style());
+                                       (*i)->set_position_lock_style (AudioTime);
                                        need_add = false;
                                } else {
+                                       delete (*i);
                                        _metrics.erase (i);
-
                                }
 
                                break;
@@ -814,7 +870,7 @@ TempoMap::do_insert (MetricSection* section)
 
                                if (meter) {
                                        bool const ipm = insert_meter->position_lock_style() == MusicTime;
-                                       if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
+                                       if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
                                                break;
                                        }
                                }
@@ -833,44 +889,40 @@ TempoMap::do_insert (MetricSection* section)
                }
 
                _metrics.insert (i, section);
-               //dump (_metrics, std::cerr);
+               //dump (_metrics, std::cout);
        }
 }
 
-void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
+TempoSection*
+TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
 {
+       TempoSection* ts = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               TempoSection& first (first_tempo());
-               if (ts.pulse() != first.pulse()) {
-                       remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, pulse, true, type);
-               } else {
-                       first.set_type (type);
-                       {
-                               /* cannot move the first tempo section */
-                               *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (_metrics);
-                       }
-               }
+               ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
        }
 
+
        PropertyChanged (PropertyChange ());
+
+       return ts;
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
 {
+       const bool locked_to_meter = ts.locked_to_meter();
+
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
                if (ts.frame() != first.frame()) {
                        remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, frame, true, type);
+                       add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
                } else {
                        first.set_type (type);
                        first.set_pulse (0.0);
+                       first.set_frame (frame);
                        first.set_position_lock_style (AudioTime);
                        {
                                /* cannot move the first tempo section */
@@ -883,87 +935,57 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
        PropertyChanged (PropertyChange ());
 }
 
-void
-TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
-{
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               add_tempo_locked (tempo, pulse, true, type);
-       }
-
-       PropertyChanged (PropertyChange ());
-}
-
-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);
-       }
-
-
-       PropertyChanged (PropertyChange ());
-}
-
-void
-TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
+TempoSection*
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
+                           , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
 {
-       TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
+       TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
+       t->set_locked_to_meter (locked_to_meter);
 
-       do_insert (ts);
+       do_insert (t);
 
        if (recompute) {
-               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
+               if (pls == AudioTime) {
+                       solve_map_frame (_metrics, t, t->frame());
+               } else {
+                       solve_map_pulse (_metrics, t, t->pulse());
+               }
+               recompute_meters (_metrics);
        }
-}
-
-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) {
-               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
-       }
+       return t;
 }
 
-void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
+MeterSection*
+TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
 {
+       MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
+               m = add_meter_locked (meter, beat, where, frame, pls, true);
+       }
 
-               if (ms.movable()) {
-                       remove_meter_locked (ms);
-                       add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
-               } else {
-                       MeterSection& first (first_meter());
-                       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);
-               }
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
+               dump (_metrics, std::cerr);
        }
+#endif
 
        PropertyChanged (PropertyChange ());
+       return m;
 }
 
 void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-
-               const double beat = ms.beat();
-               const BBT_Time bbt = ms.bbt();
+               const double beat = bbt_to_beats_locked (_metrics, where);
 
                if (ms.movable()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, frame, beat, bbt, true);
+                       add_meter_locked (meter, beat, where, frame, pls, true);
                } else {
                        MeterSection& first (first_meter());
                        TempoSection& first_t (first_tempo());
@@ -977,92 +999,37 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const frame
                        first_t.set_frame (first.frame());
                        first_t.set_pulse (0.0);
                        first_t.set_position_lock_style (AudioTime);
-
-                       recompute_map (_metrics);
                }
+               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, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
+MeterSection*
+TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
 {
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, frame, beat, where, true);
-       }
+       const MeterSection& prev_m = meter_section_at_locked  (_metrics, frame - 1);
+       const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
 
-
-#ifndef NDEBUG
-       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (_metrics, std::cerr);
+       if (pls == AudioTime) {
+               /* add meter-locked tempo */
+               add_tempo_locked (tempo_at_locked (_metrics, frame), pulse,  frame, TempoSection::Ramp, AudioTime, true, true);
        }
-#endif
-
-       PropertyChanged (PropertyChange ());
-}
-
-void
-TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
-{
-       /* a new meter always starts a new bar on the first beat. so
-          round the start time appropriately. remember that
-          `where' is based on the existing tempo map, not
-          the result after we insert the new meter.
-
-       */
 
-       if (where.beats != 1) {
-               where.beats = 1;
-               where.bars++;
-       }
-       /* new meters *always* start on a beat. */
-       where.ticks = 0;
-       double pulse = pulse_at_beat_locked (_metrics, beat);
+       MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
 
-       MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
        do_insert (new_meter);
 
        if (recompute) {
-               solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
-       }
-
-}
-
-void
-TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
-{
-
-       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);
+               if (pls == AudioTime) {
+                       solve_map_frame (_metrics, new_meter, frame);
+               } else {
+                       solve_map_bbt (_metrics, new_meter, where);
+               }
        }
 
+       return new_meter;
 }
 
 void
@@ -1219,7 +1186,7 @@ TempoMap::first_tempo ()
 void
 TempoMap::recompute_tempos (Metrics& metrics)
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
@@ -1228,24 +1195,37 @@ TempoMap::recompute_tempos (Metrics& metrics)
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_ts) {
+                       if (!t->movable()) {
+                               if (!prev_t) {
+                                       t->set_pulse (0.0);
+                                       prev_t = t;
+                                       continue;
+                               }
+                       }
+                       if (prev_t) {
                                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));
+                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       if (!t->locked_to_meter()) {
+                                               t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       }
 
                                } else {
-                                       prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
-                                       t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
 
                                }
                        }
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
-       prev_ts->set_c_func (0.0);
+       prev_t->set_c_func (0.0);
 }
 
-/* tempos must be positioned correctly */
+/* tempos must be positioned correctly.
+   the current approach is to use a meter's bbt time as its base position unit.
+   an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
+   while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
+*/
 void
 TempoMap::recompute_meters (Metrics& metrics)
 {
@@ -1257,27 +1237,61 @@ TempoMap::recompute_meters (Metrics& metrics)
                        if (meter->position_lock_style() == AudioTime) {
                                double pulse = 0.0;
                                pair<double, BBT_Time> b_bbt;
-                               if (meter->movable()) {
-                                       const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
-                                       const double ceil_beats = beats - fmod (beats, prev_m->divisions_per_bar());
-                                       b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 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;
+                               TempoSection* meter_locked_tempo = 0;
+                               for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
+                                       TempoSection* t;
+                                       if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
+                                                       meter_locked_tempo = t;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               if (prev_m) {
+                                       const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+                                       if (beats + prev_m->beat() != meter->beat()) {
+                                               /* reordering caused a bbt change */
+                                               b_bbt = make_pair (beats + prev_m->beat()
+                                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+
+                                       } else if (meter->movable()) {
+                                               b_bbt = make_pair (meter->beat(), meter->bbt());
+                                               pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+                                       }
                                } else {
                                        b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                }
+                               if (meter_locked_tempo) {
+                                       meter_locked_tempo->set_pulse (pulse);
+                               }
                                meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
+
                        } else {
+                               /* MusicTime */
                                double pulse = 0.0;
+                               pair<double, BBT_Time> new_beat;
                                if (prev_m) {
-                                       pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
+                                       const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+                                       if (beats + prev_m->beat() != meter->beat()) {
+                                               /* reordering caused a bbt change */
+                                               new_beat = make_pair (beats + prev_m->beat()
+                                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                       } else {
+                                               new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
+                                       }
+                                       pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                } else {
+                                       /* shouldn't happen - the first is audio-locked */
                                        pulse = pulse_at_beat_locked (metrics, meter->beat());
+                                       new_beat = make_pair (meter->beat(), meter->bbt());
                                }
-                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+
+                               meter->set_beat (new_beat);
                                meter->set_pulse (pulse);
+                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
                        }
 
                        prev_m = meter;
@@ -1371,98 +1385,108 @@ TempoMap::metric_at (BBT_Time bbt) const
 }
 
 double
-TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+TempoMap::pulse_at_beat (const double& beat) const
 {
-       /* HOLD (at least) THE READER LOCK */
-       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 (!t->active()) {
-                               continue;
-                       }
-                       if (prev_ts && t->frame() > frame) {
-                               /*the previous ts is the one containing the frame */
-                               const double ret = prev_ts->pulse_at_frame (frame, _frame_rate);
-                               return ret;
-                       }
-                       accumulated_pulses = t->pulse();
-                       prev_ts = t;
-               }
-       }
-
-       /* treated as constant for this ts */
-       const double pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
-
-       return pulses_in_section + accumulated_pulses;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return pulse_at_beat_locked (_metrics, beat);
 }
 
 double
 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
-       MeterSection* prev_ms = 0;
-       double accumulated_beats = 0.0;
+       MeterSection* prev_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms && m->beat() > beat) {
+                       if (prev_m && m->beat() > beat) {
                                break;
                        }
-                       accumulated_beats = m->beat();
-                       prev_ms = m;
+                       prev_m = m;
                }
 
        }
-       double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
+       double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
        return ret;
 }
 
 double
-TempoMap::pulse_at_beat (const double& beat) const
+TempoMap::beat_at_pulse (const double& pulse) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_beat_locked (_metrics, beat);
+       return beat_at_pulse_locked (_metrics, pulse);
 }
 
 double
 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
-       MeterSection* prev_ms = 0;
-       double accumulated_beats = 0.0;
+       MeterSection* prev_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms && m->pulse() > pulse) {
-                               break;
+                       if (prev_m && m->pulse() > pulse) {
+                               if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
+                                       break;
+                               }
                        }
-                       accumulated_beats = m->beat();
-                       prev_ms = m;
+                       prev_m = m;
                }
        }
 
-       double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
+       double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
+       return ret;
+}
 
-       return beats_in_section + accumulated_beats;
+double
+TempoMap::pulse_at_frame (const framecnt_t& frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return pulse_at_frame_locked (_metrics, frame);
 }
 
+/* tempo section based */
 double
-TempoMap::beat_at_pulse (const double& pulse) const
+TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+{
+       /* HOLD (at least) THE READER LOCK */
+       TempoSection* prev_t = 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_t && t->frame() > frame) {
+                               /*the previous ts is the one containing the frame */
+                               const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
+                               return ret;
+                       }
+                       prev_t = t;
+               }
+       }
+
+       /* treated as constant for this ts */
+       const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
+
+       return pulses_in_section + prev_t->pulse();
+}
+
+framecnt_t
+TempoMap::frame_at_pulse (const double& pulse) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_pulse_locked (_metrics, pulse);
+       return frame_at_pulse_locked (_metrics, pulse);
 }
 
+/* tempo section based */
 framecnt_t
 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
        /* HOLD THE READER LOCK */
 
-       const TempoSection* prev_ts = 0;
-       double accumulated_pulses = 0.0;
+       const TempoSection* prev_t = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
@@ -1471,30 +1495,22 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_ts && t->pulse() > pulse) {
-                               return prev_ts->frame_at_pulse (pulse, _frame_rate);
+                       if (prev_t && t->pulse() > pulse) {
+                               return prev_t->frame_at_pulse (pulse, _frame_rate);
                        }
 
-                       accumulated_pulses = t->pulse();
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
        /* must be treated as constant, irrespective of _type */
-       double const pulses_in_section = pulse - accumulated_pulses;
-       double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
+       double const pulses_in_section = pulse - prev_t->pulse();
+       double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
 
-       framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
+       framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
 
        return ret;
 }
 
-double
-TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
-{
-       const double pulse = pulse_at_frame_locked (metrics, frame);
-       return beat_at_pulse_locked (metrics, pulse);
-}
-
 double
 TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
@@ -1502,12 +1518,35 @@ TempoMap::beat_at_frame (const framecnt_t& frame) const
        return beat_at_frame_locked (_metrics, frame);
 }
 
-framecnt_t
-TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+/* meter / tempo section based */
+double
+TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
 {
-       const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
-       return frame;
-}
+       const TempoSection& ts = tempo_section_at_locked (metrics, frame);
+       MeterSection* prev_m = 0;
+       MeterSection* next_m = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_m && m->frame() > frame) {
+                               next_m = m;
+                               break;
+                       }
+                       prev_m = m;
+               }
+       }
+       if (frame < prev_m->frame()) {
+               return 0.0;
+       }
+       const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+
+       if (next_m && next_m->beat() < beat) {
+               return next_m->beat();
+       }
+
+       return beat;
+}
 
 framecnt_t
 TempoMap::frame_at_beat (const double& beat) const
@@ -1516,14 +1555,40 @@ TempoMap::frame_at_beat (const double& beat) const
        return frame_at_beat_locked (_metrics, beat);
 }
 
+/* meter section based */
+framecnt_t
+TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
+       MeterSection* prev_m = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_m && m->beat() > beat) {
+                               break;
+                       }
+                       prev_m = m;
+               }
+       }
+
+       return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
+}
+
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return bbt_to_beats_locked (_metrics, bbt);
+}
+
+
 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;
+       MeterSection* prev_m = 0;
 
        /* because audio-locked meters have 'fake' integral beats,
           there is no pulse offset here.
@@ -1531,64 +1596,56 @@ TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time&
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms) {
-                               const double bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
-                               if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
+                       if (prev_m) {
+                               const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
+                               if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
                                        break;
                                }
-                               accumulated_beats = m->beat();
-                               accumulated_bars += bars_to_m;
                        }
-                       prev_ms = m;
+                       prev_m = m;
                }
        }
 
-       const double remaining_bars = (bbt.bars - 1) - accumulated_bars;
-       const double remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
-       const double ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+       const double remaining_bars = bbt.bars - prev_m->bbt().bars;
+       const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
+       const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
 
        return ret;
 }
 
-double
-TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
+Timecode::BBT_Time
+TempoMap::beats_to_bbt (const double& beats)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_to_beats_locked (_metrics, bbt);
+       return beats_to_bbt_locked (_metrics, beats);
 }
 
 Timecode::BBT_Time
 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
 {
        /* CALLER HOLDS READ LOCK */
-       MeterSection* prev_ms = 0;
-       const double beats = (b < 0.0) ? 0.0 : b;
-       uint32_t accumulated_bars = 0;
-       double accumulated_beats = 0.0;
+       MeterSection* prev_m = 0;
+       const double beats = max (0.0, b);
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m = 0;
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms) {
+                       if (prev_m) {
                                if (m->beat() > beats) {
                                        /* this is the meter after the one our beat is on*/
                                        break;
                                }
-                               const double beats_to_m = m->beat() - prev_ms->beat();
-                               /* 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;
+                       prev_m = m;
                }
        }
 
-       const double beats_in_ms = beats - accumulated_beats;
-       const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
-       const uint32_t total_bars = bars_in_ms + accumulated_bars;
-       const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
+       const double beats_in_ms = beats - prev_m->beat();
+       const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
+       const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
+       const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
        const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
        BBT_Time ret;
@@ -1606,7 +1663,7 @@ TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
                ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
+       if (ret.beats >= prev_m->divisions_per_bar() + 1) {
                ++ret.bars;
                ret.beats = 1;
        }
@@ -1614,45 +1671,33 @@ TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
        return ret;
 }
 
-Timecode::BBT_Time
-TempoMap::beats_to_bbt (const double& beats)
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beats_to_bbt_locked (_metrics, beats);
-}
-
 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;
+       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 (prev_ms) {
-                               double const pulses_to_m = m->pulse() - prev_ms->pulse();
-                               if (accumulated_pulses + pulses_to_m > pulse) {
+                       if (prev_m) {
+                               double const pulses_to_m = m->pulse() - prev_m->pulse();
+                               if (prev_m->pulse() + pulses_to_m > pulse) {
                                        /* this is the meter after the one our beat is on*/
                                        break;
                                }
-
-                               /* 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;
+                       prev_m = m;
                }
        }
-       const double beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
-       const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
-       const uint32_t total_bars = bars_in_ms + accumulated_bars;
-       const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
+
+       const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
+       const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
+       const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
+       const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
        const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
        BBT_Time ret;
@@ -1670,7 +1715,7 @@ TempoMap::pulse_to_bbt (const double& pulse)
                ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
+       if (ret.beats >= prev_m->divisions_per_bar() + 1) {
                ++ret.bars;
                ret.beats = 1;
        }
@@ -1695,15 +1740,6 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
        bbt = beats_to_bbt_locked (_metrics, beat);
 }
 
-framepos_t
-TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
-{
-       /* HOLD THE READER LOCK */
-
-       const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
-       return ret;
-}
-
 framepos_t
 TempoMap::frame_time (const BBT_Time& bbt)
 {
@@ -1716,39 +1752,62 @@ TempoMap::frame_time (const BBT_Time& bbt)
                throw std::logic_error ("beats are counted from one");
        }
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       const double beat = bbt_to_beats_locked (_metrics, bbt);
-       const framecnt_t frame = frame_at_beat_locked (_metrics, beat);
 
-       return frame;
+       return frame_time_locked (_metrics, bbt);
+}
+
+/* meter section based */
+framepos_t
+TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
+{
+       /* HOLD THE READER LOCK */
+
+       const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
+       return ret;
 }
 
 bool
-TempoMap::check_solved (Metrics& metrics, bool by_frame)
+TempoMap::check_solved (const Metrics& metrics) const
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
+       MeterSection* prev_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
+               MeterSection* m;
                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())) {
+                       if (prev_t) {
+                               if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
                                        return false;
                                }
 
-                               if (t->frame() == prev_ts->frame()) {
-                                       return false;
+                               /* precision check ensures pulses and frames align.*/
+                               if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
+                                       if (!t->locked_to_meter()) {
+                                               return false;
+                                       }
                                }
+                       }
+                       prev_t = t;
+               }
+
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_m && m->position_lock_style() == AudioTime) {
+                               TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
+                               const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
+                               const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
 
-                               /* 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)) {
+                               if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
                                        return false;
                                }
                        }
-                       prev_ts = t;
+
+                       prev_m = m;
                }
+
        }
 
        return true;
@@ -1778,9 +1837,9 @@ TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
+TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
        framepos_t first_m_frame = 0;
 
@@ -1795,9 +1854,9 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
        }
        if (section->movable() && frame <= first_m_frame) {
                return false;
-       } else {
-               section->set_active (true);
        }
+
+       section->set_active (true);
        section->set_frame (frame);
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
@@ -1807,80 +1866,54 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_ts) {
+                       if (prev_t) {
                                if (t == section) {
-                                       section_prev = prev_ts;
+                                       section_prev = prev_t;
                                        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));
+                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_t->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_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       if (!t->locked_to_meter()) {
+                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                       }
                                }
                        }
-                       prev_ts = t;
+                       prev_t = 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->locked_to_meter()) {
+                       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);
-       }
+       recompute_tempos (imaginary);
 
-       if (check_solved (imaginary, true)) {
-               recompute_meters (imaginary);
+       if (check_solved (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);
+       recompute_tempos (imaginary);
+
+       if (check_solved (imaginary)) {
                return true;
        }
-       //dump (imaginary, std::cerr);
 
        return false;
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
+TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
 
        section->set_pulse (pulse);
@@ -1891,289 +1924,388 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_ts) {
+                       if (!t->movable()) {
+                               t->set_pulse (0.0);
+                               prev_t = t;
+                               continue;
+                       }
+                       if (prev_t) {
                                if (t == section) {
-                                       section_prev = prev_ts;
+                                       section_prev = prev_t;
                                        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));
+                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       t->set_frame (prev_t->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_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       if (!t->locked_to_meter()) {
+                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                       }
                                }
                        }
-                       prev_ts = t;
+                       prev_t = 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);
+       recompute_tempos (imaginary);
+
+       if (check_solved (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);
+       recompute_tempos (imaginary);
+       /* Reordering
+        * XX need a restriction here, but only for this case,
+        * as audio locked tempos don't interact in the same way.
+        *
+        * With music-locked tempos, the solution to cross-dragging can fly off the screen
+        * e.g.
+        * |50 bpm                        |250 bpm |60 bpm
+        *                drag 250 to the pulse after 60->
+        * a clue: dragging the second 60 <- past the 250 would cause no such problem.
+        */
+       if (check_solved (imaginary)) {
                return true;
        }
 
-       //dump (imaginary, std::cerr);
-
        return false;
 }
 
-void
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
+bool
+TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
 {
-       MeterSection* prev_ms = 0;
+       /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
+       const MeterSection* other =  &meter_section_at_locked (imaginary, frame);
+       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
+               return false;
+       }
 
        if (!section->movable()) {
                /* lock the first tempo to our first meter */
                if (!set_active_tempos (imaginary, frame)) {
-                       return;
+                       return false;
                }
-               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;
+       /* it would make sense to bail out if there is no audio-locked meter,
+          however it may be desirable to move a music-locked meter by frame at some point.
+       */
+       TempoSection* meter_locked_tempo = 0;
+       for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+                       if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
+                               meter_locked_tempo = t;
+                               break;
+                       }
                }
        }
 
-       section->set_frame (frame);
+       if (!meter_locked_tempo) {
+               return false;
+       }
+
+       MeterSection* prev_m = 0;
+       Metrics future_map;
+       TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+       bool solved = false;
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
                        if (m == section){
-                               /*
-                                 here we set the beat for this frame.
-                                 we set it 'incorrectly' to the next bar's beat
-                                 and use this difference to find the meter's pulse.
-                               */
-                               double pulse = 0.0;
-                               pair<double, BBT_Time> b_bbt;
-                               if (m->movable()) {
-                                       const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
-                                       const double ceil_beats = beats - fmod (beats,  prev_ms->divisions_per_bar());
-                                       b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 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 (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);
+                               if (prev_m && section->movable()) {
+                                       const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
+                                       if (beats + prev_m->beat() < section->beat()) {
+                                               /* set the frame/pulse corresponding to its musical position,
+                                                * as an earlier time than this has been requested.
+                                               */
+                                               const double new_pulse = ((section->beat() - prev_m->beat())
+                                                                         / prev_m->note_divisor()) + prev_m->pulse();
+
+                                               const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
+
+                                               if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
+                                                       meter_locked_tempo->set_pulse (new_pulse);
+                                                       solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
+                                                       section->set_frame (smallest_frame);
+                                                       section->set_pulse (new_pulse);
+                                               } else {
+                                                       solved = false;
+                                               }
+
+                                               Metrics::const_iterator d = future_map.begin();
+                                               while (d != future_map.end()) {
+                                                       delete (*d);
+                                                       ++d;
+                                               }
+
+                                               if (!solved) {
+                                                       return false;
+                                               }
+                                       } else {
+                                               /* all is ok. set section's tempo */
+                                               MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
+                                               meter_copy->set_frame (frame);
+
+                                               if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
+                                                       section->set_frame (frame);
+                                                       meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
+                                                                                               / prev_m->note_divisor()) + prev_m->pulse());
+                                                       solve_map_frame (imaginary, meter_locked_tempo, frame);
+                                               } else {
+                                                       solved = false;
+                                               }
+
+                                               Metrics::const_iterator d = future_map.begin();
+                                               while (d != future_map.end()) {
+                                                       delete (*d);
+                                                       ++d;
+                                               }
+
+                                               if (!solved) {
+                                                       return false;
+                                               }
+                                       }
                                } else {
-                                       double pulse = 0.0;
-                                       pair<double, BBT_Time> b_bbt;
-                                       if (m->movable()) {
-                                               const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
-                                               const double ceil_beats = beats - fmod (beats , prev_ms->divisions_per_bar());
-                                               b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 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;
+                                       /* not movable (first meter atm) */
+
+                                       tempo_copy->set_frame (frame);
+                                       tempo_copy->set_pulse (0.0);
+
+                                       if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
+                                               section->set_frame (frame);
+                                               meter_locked_tempo->set_frame (frame);
+                                               meter_locked_tempo->set_pulse (0.0);
+                                               solve_map_frame (imaginary, meter_locked_tempo, frame);
                                        } else {
-                                               b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
-                                               pulse = 0.0;
+                                               solved = false;
+                                       }
+
+                                       Metrics::const_iterator d = future_map.begin();
+                                       while (d != future_map.end()) {
+                                               delete (*d);
+                                               ++d;
                                        }
-                                       m->set_beat (b_bbt);
-                                       m->set_pulse (pulse);
+
+                                       if (!solved) {
+                                               return false;
+                                       }
+
+                                       pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                                       section->set_beat (b_bbt);
+                                       section->set_pulse (0.0);
+
                                }
+                               break;
                        }
-                       prev_ms = m;
+
+                       prev_m = m;
                }
        }
 
-       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);
+       MetricSectionFrameSorter fcmp;
+       imaginary.sort (fcmp);
+
+       recompute_meters (imaginary);
+
+       return true;
 }
 
-void
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
+bool
+TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
 {
-       MeterSection* prev_ms = 0;
+       /* disallow setting section to an existing meter's bbt */
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (m != section && m->bbt().bars == when.bars) {
+                               return false;
+                       }
+               }
+       }
 
-       section->set_pulse (pulse);
+       MeterSection* prev_m = 0;
+       MeterSection* section_prev = 0;
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (m == section){
-                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
-                               const double beats = ((pulse - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
-                               const int32_t bars = (beats + 1) / prev_ms->divisions_per_bar();
-                               pair<double, BBT_Time> b_bbt = make_pair (beats, BBT_Time (bars + 1, 1, 0));
+                       pair<double, BBT_Time> b_bbt;
+                       double new_pulse = 0.0;
+
+                       if (prev_m && m->bbt().bars > when.bars && !section_prev){
+                               section_prev = prev_m;
+                               const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
+                               const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
+                               pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
+
                                section->set_beat (b_bbt);
-                               prev_ms = section;
+                               section->set_pulse (pulse);
+                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                               prev_m = section;
                                continue;
                        }
-                       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 {
-                                       double pulse = 0.0;
-                                       pair<double, BBT_Time> b_bbt;
-                                       if (m->movable()) {
-                                               const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
-                                               const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
-                                               b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
-                                               const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
-                                               const double pulse_off = true_pulse - ((ceil_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));
+
+                       if (m->position_lock_style() == AudioTime) {
+                               TempoSection* meter_locked_tempo = 0;
+
+                               for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
+                                       TempoSection* t;
+                                       if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
+                                                       meter_locked_tempo = t;
+                                                       break;
+                                               }
                                        }
-                                       m->set_beat (b_bbt);
-                                       m->set_pulse (pulse);
                                }
+
+                               if (!meter_locked_tempo) {
+                                       return false;
+                               }
+
+                               if (prev_m) {
+                                       const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
+
+                                       if (beats + prev_m->beat() != m->beat()) {
+                                               /* tempo/ meter change caused a change in beat (bar). */
+                                               b_bbt = make_pair (beats + prev_m->beat()
+                                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+                                       } else if (m->movable()) {
+                                               b_bbt = make_pair (m->beat(), m->bbt());
+                                               new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+                                       }
+                               } else {
+                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                               }
+
+                               meter_locked_tempo->set_pulse (new_pulse);
+                               m->set_beat (b_bbt);
+                               m->set_pulse (new_pulse);
+
+                       } else {
+                               /* MusicTime */
+                               const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
+                               if (beats + prev_m->beat() != m->beat()) {
+                                       /* tempo/ meter change caused a change in beat (bar). */
+                                       b_bbt = make_pair (beats + prev_m->beat()
+                                                          , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                               } else {
+                                       b_bbt = make_pair (beats + prev_m->beat()
+                                                          , m->bbt());
+                               }
+                               new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
+                               m->set_beat (b_bbt);
+                               m->set_pulse (new_pulse);
+                               m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
                        }
-                       prev_ms = m;
+
+                       prev_m = m;
                }
        }
 
-       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);
+       if (!section_prev) {
+
+               const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+               const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
+               pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
+
+               section->set_beat (b_bbt);
+               section->set_pulse (pulse);
+               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
        }
+
+       MetricSectionSorter cmp;
+       imaginary.sort (cmp);
+
+       recompute_meters (imaginary);
+
+       return true;
 }
 
 /** places a copy of _metrics into copy and returns a pointer
- *  to section's equivalent.
+ *  to section's equivalent in copy.
  */
 TempoSection*
-TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
+TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
 {
-       TempoSection* t;
        TempoSection* ret = 0;
-       MeterSection* m;
 
-       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               MeterSection* m;
                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());
+                               ret = new TempoSection (*t);
                                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());
+
+                       TempoSection* cp = new TempoSection (*t);
                        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());
-                               cp->set_frame (m->frame());
-                       } else {
-                               cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
-                               cp->set_pulse (m->pulse());
+                       MeterSection* cp = new MeterSection (*m);
+                       copy.push_back (cp);
+               }
+       }
+
+       return ret;
+}
+
+MeterSection*
+TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
+{
+       MeterSection* ret = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               MeterSection* m;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       TempoSection* cp = new TempoSection (*t);
+                       copy.push_back (cp);
+               }
+
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       if (m == section) {
+                               ret = new MeterSection (*m);
+                               copy.push_back (ret);
+                               continue;
                        }
-                       cp->set_movable (m->movable());
+                       MeterSection* cp = new MeterSection (*m);
                        copy.push_back (cp);
                }
        }
-       //recompute_map (copy);
+
        return ret;
 }
 
 bool
-TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
+TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
 {
        Metrics copy;
-       TempoSection* new_section = 0;
+       TempoSection* tempo_copy = 0;
 
        {
                Glib::Threads::RWLock::ReaderLock lm (lock);
-               new_section = copy_metrics_and_point (copy, ts);
+               tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
+               if (!tempo_copy) {
+                       return false;
+               }
        }
 
-       double const beat = bbt_to_beats_locked (copy, bbt);
-       bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
+       const double beat = bbt_to_beats_locked (copy, bbt);
+       const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
 
        Metrics::const_iterator d = copy.begin();
        while (d != copy.end()) {
@@ -2185,48 +2317,30 @@ TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bb
 }
 
 /**
-* 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,
+* This is for a gui that needs to know the pulse or 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.
+* @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
 */
-framepos_t
-TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
+pair<double, framepos_t>
+TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
        Metrics future_map;
-       framepos_t ret = 0;
-       TempoSection* new_section = copy_metrics_and_point (future_map, section);
-
-       const double beat = bbt_to_beats_locked (future_map, bbt);
-       if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
-               ret = new_section->frame();
-       } else {
-               ret = frame_at_beat_locked (_metrics, beat);
-       }
-
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
-       }
-       return ret;
-}
+       pair<double, framepos_t> ret = make_pair (0.0, 0);
 
-double
-TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
-{
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics future_map;
-       double ret = 0.0;
-       TempoSection* new_section = copy_metrics_and_point (future_map, section);
 
-       if (solve_map (future_map, new_section, bpm, frame)) {
-               ret = new_section->pulse();
+       TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
+
+       const double beat = bbt_to_beats_locked (future_map, bbt);
+
+       if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+               ret.first = tempo_copy->pulse();
+               ret.second = tempo_copy->frame();
        } else {
-               ret = pulse_at_frame_locked (_metrics, frame);
+               ret.first = section->pulse();
+               ret.second = section->frame();
        }
 
        Metrics::const_iterator d = future_map.begin();
@@ -2238,14 +2352,29 @@ TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const fr
 }
 
 void
-TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
+TempoMap::gui_move_tempo (TempoSection* ts, const pair<double, framepos_t>& pulse)
 {
        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);
+
+       if (ts->position_lock_style() == MusicTime) {
+               {
+                       Glib::Threads::RWLock::WriterLock lm (lock);
+                       TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+                       if (solve_map_pulse (future_map, tempo_copy, pulse.first)) {
+                               solve_map_pulse (_metrics, ts, pulse.first);
+                               recompute_meters (_metrics);
+                       }
+               }
+
+       } else {
+
+               {
+                       Glib::Threads::RWLock::WriterLock lm (lock);
+                       TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+                       if (solve_map_frame (future_map, tempo_copy, pulse.second)) {
+                               solve_map_frame (_metrics, ts, pulse.second);
+                               recompute_meters (_metrics);
+                       }
                }
        }
 
@@ -2259,14 +2388,33 @@ TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const frame
 }
 
 void
-TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double& beat)
+TempoMap::gui_move_meter (MeterSection* ms, 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, pulse_at_beat_locked (future_map, beat))) {
-                       solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
+
+       if (ms->position_lock_style() == AudioTime) {
+
+               {
+                       Glib::Threads::RWLock::WriterLock lm (lock);
+                       MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
+
+                       if (solve_map_frame (future_map, copy, frame)) {
+                               solve_map_frame (_metrics, ms, frame);
+                               recompute_tempos (_metrics);
+                       }
+               }
+       } else {
+               {
+                       Glib::Threads::RWLock::WriterLock lm (lock);
+                       MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
+
+                       const double beat = beat_at_frame_locked (_metrics, frame);
+                       const Timecode::BBT_Time bbt = beats_to_bbt_locked (_metrics, beat);
+
+                       if (solve_map_bbt (future_map, copy, bbt)) {
+                               solve_map_bbt (_metrics, ms, bbt);
+                               recompute_tempos (_metrics);
+                       }
                }
        }
 
@@ -2279,40 +2427,18 @@ TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double
        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)
+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());
+               TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+               tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
                recompute_tempos (future_map);
 
-               if (check_solved (future_map, true)) {
+               if (check_solved (future_map)) {
                        ts->set_beats_per_minute (bpm.beats_per_minute());
                        recompute_map (_metrics);
                        can_solve = true;
@@ -2330,6 +2456,129 @@ TempoMap::gui_change_tempo (TempoSection* ts,  const Tempo& bpm)
        return can_solve;
 }
 
+void
+TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
+{
+       /*
+         Ts (future prev_t)   Tnext
+         |                    |
+         |     [drag^]        |
+         |----------|----------
+               e_f  pulse(frame)
+       */
+
+       Metrics future_map;
+
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+
+               if (!ts) {
+                       return;
+               }
+
+               TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
+               TempoSection* prev_to_prev_t = 0;
+               const frameoffset_t fr_off = end_frame - frame;
+
+               if (prev_t && prev_t->pulse() > 0.0) {
+                       prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
+               }
+
+               TempoSection* next_t = 0;
+               for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
+                       TempoSection* t = 0;
+                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                               if (t->frame() > ts->frame()) {
+                                       next_t = t;
+                                       break;
+                               }
+                       }
+               }
+
+               /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
+                  constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
+               */
+               double contribution = 0.0;
+               double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
+
+               if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+                       contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
+               }
+
+               frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
+               double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+               double new_bpm;
+
+               if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
+
+                       if (prev_t->position_lock_style() == MusicTime) {
+                               if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+
+                                       new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
+                                                                               / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
+
+                               } else {
+                                       /* prev to prev is irrelevant */
+
+                                       if (start_pulse != prev_t->pulse()) {
+                                               new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
+                                       } else {
+                                               new_bpm = prev_t->beats_per_minute();
+                                       }
+                               }
+                       } else {
+                               /* AudioTime */
+                               if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+                                       new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
+                                                                               / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
+                               } else {
+                                       /* prev_to_prev_t is irrelevant */
+
+                                       if (end_frame != prev_t->frame()) {
+                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
+                                       } else {
+                                               new_bpm = prev_t->beats_per_minute();
+                                       }
+                               }
+                       }
+               } else {
+
+                       double frame_ratio;
+                       double pulse_ratio;
+                       const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
+
+                       if (prev_to_prev_t) {
+
+                               frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
+                               pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
+                       } else {
+
+                               frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
+                               pulse_ratio = (start_pulse / end_pulse);
+                       }
+                       new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
+               }
+
+               prev_t->set_beats_per_minute (new_bpm);
+               recompute_tempos (future_map);
+               recompute_meters (future_map);
+
+               if (check_solved (future_map)) {
+                       ts->set_beats_per_minute (new_bpm);
+                       recompute_tempos (_metrics);
+                       recompute_meters (_metrics);
+               }
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       MetricPositionChanged (); // Emit Signal
+}
+
 framecnt_t
 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
@@ -2338,10 +2587,8 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
        const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
        const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
        const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
-       const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
-       const framecnt_t ret = time_at_bbt;
 
-       return ret;
+       return frame_at_beat_locked (_metrics, total_beats);
 }
 
 framepos_t
@@ -2453,18 +2700,29 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 }
 
 void
-TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
+TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
 {
        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) {
+               if (dir > 0) {
                        ++when.bars;
+                       when.beats = 1;
+                       when.ticks = 0;
+               } else if (dir < 0) {
+                       when.beats = 1;
+                       when.ticks = 0;
+               } else {
+                       const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+                       if ((double) when.beats > bpb / 2.0) {
+                               ++when.bars;
+                       }
+                       when.beats = 1;
+                       when.ticks = 0;
                }
-               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();
+               const double bpb = meter_section_at_beat (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) {
@@ -2473,32 +2731,88 @@ TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
                        }
                }
                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 (dir > 0) {
+               /* round to next (or same iff dir == RoundUpMaybe) */
+
+               uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
+
+               if (mod == 0 && dir == RoundUpMaybe) {
+                       /* right on the subdivision, which is fine, so do nothing */
+
+               } else if (mod == 0) {
+                       /* right on the subdivision, so the difference is just the subdivision ticks */
+                       when.ticks += ticks_one_subdivisions_worth;
+
+               } else {
+                       /* not on subdivision, compute distance to next subdivision */
 
-               if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
+                       when.ticks += ticks_one_subdivisions_worth - mod;
+               }
+
+               if (when.ticks >= BBT_Time::ticks_per_beat) {
                        ++when.beats;
-                       when.ticks -= Timecode::BBT_Time::ticks_per_beat;
+                       const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+                       if ((double) when.beats > bpb) {
+                               ++when.bars;
+                               when.beats = 1;
+                       }
+                       when.ticks -= BBT_Time::ticks_per_beat;
                }
 
-       } else if (rem > 0) {
-               /* closer to previous subdivision, so shift backward */
+       } else if (dir < 0) {
+               /* round to previous (or same iff dir == RoundDownMaybe) */
 
-               if (rem > when.ticks) {
-                       if (when.beats == 0) {
-                               /* can't go backwards past zero, so ... */
-                       }
-                       /* step back to previous beat */
+               uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
+
+               if (difference == 0 && dir == RoundDownAlways) {
+                       /* right on the subdivision, but force-rounding down,
+                          so the difference is just the subdivision ticks */
+                       difference = ticks_one_subdivisions_worth;
+               }
+
+               if (when.ticks < difference) {
                        --when.beats;
-                       when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
+                       const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+                       if ((double) when.beats < bpb) {
+                               --when.bars;
+                               //when.beats = 1;
+                       }
+                       when.ticks = BBT_Time::ticks_per_beat - when.ticks;
                } else {
-                       when.ticks = when.ticks - rem;
+                       when.ticks -= difference;
+               }
+
+       } else {
+               /* round to nearest */  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;
+                       }
                }
        }
 }
@@ -2517,22 +2831,22 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                        /* find bar previous to 'frame' */
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_time (bbt);
+                       return frame_time_locked (_metrics, bbt);
 
                } else if (dir > 0) {
                        /* find bar following 'frame' */
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_time (bbt);
+                       return frame_time_locked (_metrics, bbt);
                } else {
                        /* true rounding: find nearest bar */
-                       framepos_t raw_ft = frame_time (bbt);
+                       framepos_t raw_ft = frame_time_locked (_metrics, bbt);
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       framepos_t prev_ft = frame_time (bbt);
+                       framepos_t prev_ft = frame_time_locked (_metrics, bbt);
                        ++bbt.bars;
-                       framepos_t next_ft = frame_time (bbt);
+                       framepos_t next_ft = frame_time_locked (_metrics, bbt);
 
                        if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
                                return next_ft;
@@ -2562,18 +2876,18 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    framepos_t lower, framepos_t upper)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
-       int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
+       int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
+       framecnt_t pos = 0;
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
-       while (cnt <= upper_beat) {
-               framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
-               const TempoSection tempo = tempo_section_at_locked (pos);
-               const MeterSection meter = meter_section_at_locked (pos);
+       while (pos < upper) {
+               pos = frame_at_beat_locked (_metrics, cnt);
+               const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
+               const MeterSection meter = meter_section_at_locked (_metrics, pos);
                const BBT_Time bbt = beats_to_bbt (cnt);
-               points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
+               points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
                ++cnt;
        }
 }
@@ -2582,23 +2896,23 @@ const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_section_at_locked (frame);
+       return tempo_section_at_locked (_metrics, frame);
 }
 
 const TempoSection&
-TempoMap::tempo_section_at_locked (framepos_t frame) const
+TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
 {
        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 (!t->active()) {
                                continue;
                        }
-                       if (t->frame() > frame) {
+                       if (prev && t->frame() > frame) {
                                break;
                        }
 
@@ -2614,6 +2928,42 @@ TempoMap::tempo_section_at_locked (framepos_t frame) const
        return *prev;
 }
 
+const TempoSection&
+TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       TempoSection* prev_t = 0;
+       const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+
+       }
+       return *prev_t;
+}
+
+const TempoSection&
+TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       TempoSection* prev_t = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_t && t->pulse() > pulse) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+
+       }
+       return *prev_t;
+}
 
 /* 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
@@ -2623,7 +2973,7 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const TempoSection* ts_at = &tempo_section_at_locked (frame);
+       const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
        const TempoSection* ts_after = 0;
        Metrics::const_iterator i;
 
@@ -2649,16 +2999,9 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
 }
 
 const Tempo
-TempoMap::tempo_at (const framepos_t& frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_at_locked (frame);
-}
-
-const Tempo
-TempoMap::tempo_at_locked (const framepos_t& frame) const
+TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
 
        Metrics::const_iterator i;
 
@@ -2668,37 +3011,36 @@ TempoMap::tempo_at_locked (const framepos_t& frame) const
                        if (!t->active()) {
                                continue;
                        }
-                       if ((prev_ts) && t->frame() > frame) {
+                       if ((prev_t) && t->frame() > frame) {
                                /* t is the section past frame */
-                               const double ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
-                               const Tempo ret_tempo (ret, prev_ts->note_type());
+                               const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
+                               const Tempo ret_tempo (ret_bpm, prev_t->note_type());
                                return ret_tempo;
                        }
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
 
-       const double ret = prev_ts->beats_per_minute();
-       const Tempo ret_tempo (ret, prev_ts->note_type ());
+       const double ret = prev_t->beats_per_minute();
+       const Tempo ret_tempo (ret, prev_t->note_type ());
 
        return ret_tempo;
 }
 
-const MeterSection&
-TempoMap::meter_section_at (framepos_t frame) const
+const Tempo
+TempoMap::tempo_at (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return meter_section_at_locked (frame);
+       return tempo_at_locked (_metrics, frame);
 }
 
 const MeterSection&
-TempoMap::meter_section_at_locked (framepos_t frame) const
+TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
 {
-       //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
        Metrics::const_iterator i;
        MeterSection* prev = 0;
 
-       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
@@ -2719,30 +3061,106 @@ TempoMap::meter_section_at_locked (framepos_t frame) const
        return *prev;
 }
 
-const Meter&
-TempoMap::meter_at (framepos_t frame) const
+
+const MeterSection&
+TempoMap::meter_section_at (framepos_t frame) const
 {
-       TempoMetric m (metric_at (frame));
-       return m.meter();
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_locked (_metrics, frame);
 }
 
 const MeterSection&
-TempoMap::meter_section_at (const double& beat) const
+TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
-       MeterSection* prev_ms = 0;
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       MeterSection* prev_m = 0;
 
-       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+       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) {
+                       if (prev_m && m->beat() > beat) {
                                break;
                        }
-                       prev_ms = m;
+                       prev_m = m;
                }
 
        }
-       return *prev_ms;
+       return *prev_m;
+}
+
+const MeterSection&
+TempoMap::meter_section_at_beat (double beat) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_beat_locked (_metrics, beat);
+}
+
+const Meter&
+TempoMap::meter_at (framepos_t frame) const
+{
+       TempoMetric m (metric_at (frame));
+       return m.meter();
+}
+
+void
+TempoMap::fix_legacy_session ()
+{
+       MeterSection* prev_m = 0;
+       TempoSection* prev_t = 0;
+
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               MeterSection* m;
+               TempoSection* t;
+
+               if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                       if (!m->movable()) {
+                               pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                               m->set_beat (bbt);
+                               m->set_pulse (0.0);
+                               m->set_frame (0);
+                               m->set_position_lock_style (AudioTime);
+                               prev_m = m;
+                               continue;
+                       }
+                       if (prev_m) {
+                               pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
+                                                                         + (m->bbt().beats - 1)
+                                                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat)
+                                                                         , m->bbt());
+                               m->set_beat (start);
+                               const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
+                                       + (m->bbt().beats - 1)
+                                       + (m->bbt().ticks / BBT_Time::ticks_per_beat);
+                               m->set_pulse (start_beat / prev_m->note_divisor());
+                       }
+                       prev_m = m;
+               } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
+                       if (!t->movable()) {
+                               t->set_pulse (0.0);
+                               t->set_frame (0);
+                               t->set_position_lock_style (AudioTime);
+                               prev_t = t;
+                               continue;
+                       }
+
+                       if (prev_t) {
+                               const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
+                                       + (t->legacy_bbt().beats - 1)
+                                       + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
+                               if (prev_m) {
+                                       t->set_pulse (beat / prev_m->note_divisor());
+                               } else {
+                                       /* really shouldn't happen but.. */
+                                       t->set_pulse (beat / 4.0);
+                               }
+                       }
+                       prev_t = t;
+               }
+       }
 }
 
 XMLNode&
@@ -2809,32 +3227,19 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        MetricSectionSorter cmp;
                        _metrics.sort (cmp);
                }
+
                /* check for legacy sessions where bbt was the base musical unit for tempo */
-               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-                       MeterSection* m;
+               for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        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);
+                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                               if (t->legacy_bbt().bars != 0) {
+                                       fix_legacy_session();
+                                       break;
                                }
-                               prev_ts = t;
+                               break;
                        }
                }
+
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
@@ -2843,19 +3248,19 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        if (prev != _metrics.end()) {
                                MeterSection* ms;
-                               MeterSection* prev_ms;
+                               MeterSection* prev_m;
                                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;
+                               TempoSection* prev_t;
+                               if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (prev_m->pulse() == ms->pulse()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
                                                return -1;
                                        }
-                               } 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;
+                               } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                                       if (prev_t->pulse() == ts->pulse()) {
+                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
+                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
                                                return -1;
                                        }
                                }
@@ -2877,7 +3282,7 @@ 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;
+       const TempoSection* prev_t = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
@@ -2885,11 +3290,11 @@ TempoMap::dump (const Metrics& metrics, std::ostream& o) const
                        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;
+                       if (prev_t) {
+                               o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
+                               o << "calculated   : " << prev_t->tempo_at_pulse (t->pulse()) *  prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
                        }
-                       prev_ts = t;
+                       prev_t = t;
                } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
                        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;
@@ -3145,10 +3550,10 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
        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();
+       double divisions_per_bar = meter_section_at_beat (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();
+               divisions_per_bar = meter_section_at_beat (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;