Tempo ramps - code layout, check_solved() is lock-style agnostic, remove uncalled...
[ardour.git] / libs / ardour / tempo.cc
index 07fa80d52cf69f2cc05137b694478d00b08f2ef1..a4f626d82954a6f34268f88f02ceb7c555842c71 100644 (file)
@@ -73,13 +73,13 @@ 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;
@@ -453,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;
@@ -639,8 +637,8 @@ TempoMap::TempoMap (framecnt_t fr)
        _frame_rate = fr;
        BBT_Time start (1, 1, 0);
 
-       TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
-       MeterSection *m = new MeterSection ((framepos_t) 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);
@@ -883,40 +881,36 @@ TempoMap::do_insert (MetricSection* section)
        }
 }
 
-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 */
@@ -930,97 +924,56 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
 }
 
 TempoSection*
-TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
-{
-       TempoSection* ts = 0;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               ts = add_tempo_locked (tempo, pulse, true, type);
-       }
-
-       PropertyChanged (PropertyChange ());
-
-       return ts;
-}
-
-TempoSection*
-TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
-{
-       TempoSection* ts = 0;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               ts = add_tempo_locked (tempo, frame, true, type);
-       }
-
-
-       PropertyChanged (PropertyChange ());
-
-       return ts;
-}
-
-TempoSection*
-TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
+                           , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
 {
-       TempoSection* t = 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 (t);
 
        if (recompute) {
-               solve_map (_metrics, t, t->pulse());
-               recompute_meters (_metrics);
-       }
-
-       return t;
-}
-
-TempoSection*
-TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
-{
-       TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
-
-       do_insert (t);
-
-       if (recompute) {
-               solve_map (_metrics, t, t->frame());
+               if (pls == AudioTime) {
+                       solve_map_frame (_metrics, t, t->frame());
+               } else {
+                       solve_map_pulse (_metrics, t, t->pulse());
+               }
                recompute_meters (_metrics);
        }
 
        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());
-                       /* cannot move the first meter section */
-                       *static_cast<Meter*>(&first) = meter;
-                       first.set_position_lock_style (AudioTime);
-               }
-               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());
@@ -1040,87 +993,28 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const frame
        PropertyChanged (PropertyChange ());
 }
 
-
-MeterSection*
-TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
-{
-       MeterSection* m = 0;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               m = add_meter_locked (meter, beat, where, true);
-       }
-
-
-#ifndef NDEBUG
-       if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (_metrics, std::cerr);
-       }
-#endif
-
-       PropertyChanged (PropertyChange ());
-
-       return m;
-}
-
 MeterSection*
-TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
+TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
 {
-       MeterSection* m = 0;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               m = 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 ());
-
-       return m;
-}
-
-MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, double beat, const 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.
-
-       */
-
-       const double pulse = pulse_at_beat_locked (_metrics, beat);
-       MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
+       MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
 
        do_insert (new_meter);
 
        if (recompute) {
-               solve_map (_metrics, new_meter, where);
-       }
-
-       return new_meter;
-}
 
-MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
-{
-       /* add meter-locked tempo */
-       TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
-       if (t) {
-               t->set_locked_to_meter (true);
-       }
-
-       MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
-       new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
-
-       do_insert (new_meter);
-
-       if (recompute) {
-               solve_map (_metrics, new_meter, frame);
+               if (pls == AudioTime) {
+                       solve_map_frame (_metrics, new_meter, frame);
+               } else {
+                       solve_map_bbt (_metrics, new_meter, where);
+               }
        }
 
        return new_meter;
@@ -1479,6 +1373,13 @@ TempoMap::metric_at (BBT_Time bbt) const
        return m;
 }
 
+double
+TempoMap::pulse_at_beat (const double& beat) const
+{
+       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
 {
@@ -1499,10 +1400,10 @@ TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) cons
 }
 
 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
@@ -1527,10 +1428,10 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con
 }
 
 double
-TempoMap::beat_at_pulse (const double& pulse) const
+TempoMap::pulse_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_pulse_locked (_metrics, pulse);
+       return pulse_at_frame_locked (_metrics, frame);
 }
 
 /* tempo section based */
@@ -1561,11 +1462,11 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame
        return pulses_in_section + prev_t->pulse();
 }
 
-double
-TempoMap::pulse_at_frame (const framecnt_t& frame) const
+framecnt_t
+TempoMap::frame_at_pulse (const double& pulse) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_frame_locked (_metrics, frame);
+       return frame_at_pulse_locked (_metrics, pulse);
 }
 
 /* tempo section based */
@@ -1599,11 +1500,11 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co
        return ret;
 }
 
-framecnt_t
-TempoMap::frame_at_pulse (const double& pulse) const
+double
+TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_pulse_locked (_metrics, pulse);
+       return beat_at_frame_locked (_metrics, frame);
 }
 
 /* meter section based */
@@ -1636,11 +1537,11 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
        return beat;
 }
 
-double
-TempoMap::beat_at_frame (const framecnt_t& frame) const
+framecnt_t
+TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_frame_locked (_metrics, frame);
+       return frame_at_beat_locked (_metrics, beat);
 }
 
 /* meter section based */
@@ -1663,13 +1564,14 @@ TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) cons
        return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
 }
 
-framecnt_t
-TempoMap::frame_at_beat (const double& beat) const
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_beat_locked (_metrics, beat);
+       return bbt_to_beats_locked (_metrics, bbt);
 }
 
+
 double
 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
 {
@@ -1700,11 +1602,11 @@ TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time&
        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
@@ -1712,7 +1614,7 @@ TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
 {
        /* CALLER HOLDS READ LOCK */
        MeterSection* prev_m = 0;
-       const double beats = (b < 0.0) ? 0.0 : b;
+       const double beats = max (0.0, b);
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m = 0;
@@ -1758,13 +1660,6 @@ 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)
 {
@@ -1834,16 +1729,6 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
        bbt = beats_to_bbt_locked (_metrics, beat);
 }
 
-/* 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;
-}
-
 framepos_t
 TempoMap::frame_time (const BBT_Time& bbt)
 {
@@ -1860,8 +1745,18 @@ TempoMap::frame_time (const BBT_Time& bbt)
        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 (const Metrics& metrics, bool by_frame) const
+TempoMap::check_solved (const Metrics& metrics) const
 {
        TempoSection* prev_t = 0;
        MeterSection* prev_m = 0;
@@ -1874,11 +1769,7 @@ TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
                                continue;
                        }
                        if (prev_t) {
-                               if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
-                                       return false;
-                               }
-
-                               if (t->frame() == prev_t->frame()) {
+                               if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
                                        return false;
                                }
 
@@ -1935,7 +1826,7 @@ TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
+TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
 {
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
@@ -1990,31 +1881,18 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
                }
        }
 
-       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)) {
+       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_tempos (imaginary);
+
+       if (check_solved (imaginary)) {
                return true;
        }
 
@@ -2022,7 +1900,7 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
+TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
 {
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
@@ -2058,36 +1936,33 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
                        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);
-       }
+       recompute_tempos (imaginary);
 
-       if (check_solved (imaginary, false)) {
+       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_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;
        }
 
@@ -2095,7 +1970,7 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
+TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
 {
        /* 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);
@@ -2124,7 +1999,13 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
                }
        }
 
+       if (!meter_locked_tempo) {
+               return false;
+       }
+
        MeterSection* prev_m = 0;
+       Metrics future_map;
+       bool solved = false;
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
@@ -2133,84 +2014,43 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
                                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()) {
-                                               /* disallow position change if it will alter our beat
-                                                  we allow tempo changes to do this in recompute_meters().
-                                                  blocking this is an option, but i'm not convinced that
-                                                  this is what the user would actually want.
-                                                  here we set the frame/pulse corresponding to its musical position.
+                                               /* set the frame/pulse corresponding to its musical position,
+                                                * as an earlier time than this has been requested.
                                                */
 
-                                               if (meter_locked_tempo) {
-                                                       Metrics future_map;
-                                                       bool solved = false;
-                                                       TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
-                                                       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 (future_map, tempo_copy, smallest_frame))) {
-                                                               meter_locked_tempo->set_pulse (new_pulse);
-                                                               solve_map (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;
-                                                       }
+                                               TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+                                               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;
                                                }
-                                               return false;
-                                       } else {
-                                               if (meter_locked_tempo) {
-                                                       Metrics future_map;
-                                                       bool solved = false;
 
-                                                       TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
-                                                       MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
-                                                       meter_copy->set_frame (frame);
+                                               Metrics::const_iterator d = future_map.begin();
+                                               while (d != future_map.end()) {
+                                                       delete (*d);
+                                                       ++d;
+                                               }
 
-                                                       if ((solved = solve_map (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 (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;
-                                                       }
+                                               if (!solved) {
+                                                       return false;
                                                }
-                                       }
-                               } else {
-                                       /* not movable (first meter atm) */
-                                       if (meter_locked_tempo) {
-                                               Metrics future_map;
-                                               bool solved = false;
-                                               TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+                                       } else {
 
-                                               tempo_copy->set_frame (frame);
-                                               tempo_copy->set_pulse (0.0);
+                                               TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+                                               MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
+                                               meter_copy->set_frame (frame);
 
-                                               if ((solved = solve_map (future_map, tempo_copy, frame))) {
+                                               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 (imaginary, meter_locked_tempo, 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;
                                                }
@@ -2224,10 +2064,34 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
                                                if (!solved) {
                                                        return false;
                                                }
+                                       }
+                               } else {
+                                       /* not movable (first meter atm) */
+
+                                       TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
 
+                                       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 {
+                                               solved = false;
+                                       }
+
+                                       Metrics::const_iterator d = future_map.begin();
+                                       while (d != future_map.end()) {
+                                               delete (*d);
+                                               ++d;
+                                       }
+
+                                       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);
@@ -2242,20 +2106,14 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
 
        MetricSectionFrameSorter fcmp;
        imaginary.sort (fcmp);
-       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);
-       }
+
+       recompute_meters (imaginary);
 
        return true;
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
+TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
 {
        /* disallow setting section to an existing meter's bbt */
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
@@ -2358,14 +2216,7 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time&
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
 
-       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);
-       }
+       recompute_meters (imaginary);
 
        return true;
 }
@@ -2442,7 +2293,7 @@ TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
        }
 
        const double beat = bbt_to_beats_locked (copy, bbt);
-       const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
+       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()) {
@@ -2473,7 +2324,7 @@ TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
        }
        const double beat = bbt_to_beats_locked (future_map, bbt);
 
-       if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+       if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
                ret = tempo_copy->frame();
        } else {
                ret = section->frame();
@@ -2495,7 +2346,7 @@ TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
        double ret = 0.0;
        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
 
-       if (solve_map (future_map, tempo_copy, frame)) {
+       if (solve_map_frame (future_map, tempo_copy, frame)) {
                ret = tempo_copy->pulse();
        } else {
                ret = section->pulse();
@@ -2516,8 +2367,8 @@ TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
-               if (solve_map (future_map, tempo_copy, frame)) {
-                       solve_map (_metrics, ts, frame);
+               if (solve_map_frame (future_map, tempo_copy, frame)) {
+                       solve_map_frame (_metrics, ts, frame);
                        recompute_meters (_metrics);
                }
        }
@@ -2538,8 +2389,8 @@ TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
-               if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
-                       solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
+               if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+                       solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
                        recompute_meters (_metrics);
                }
        }
@@ -2554,36 +2405,14 @@ TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
 }
 
 void
-TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
-{
-       Metrics future_map;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
-               if (solve_map (future_map, tempo_copy, pulse)) {
-                       solve_map (_metrics, ts, pulse);
-                       recompute_meters (_metrics);
-               }
-       }
-
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
-       }
-
-       MetricPositionChanged (); // Emit Signal
-}
-
-void
-TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
+TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t&  frame)
 {
        Metrics future_map;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
-               if (solve_map (future_map, copy, frame)) {
-                       solve_map (_metrics, ms, frame);
+               if (solve_map_frame (future_map, copy, frame)) {
+                       solve_map_frame (_metrics, ms, frame);
                        recompute_tempos (_metrics);
                }
        }
@@ -2598,14 +2427,14 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
 }
 
 void
-TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
+TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
 {
        Metrics future_map;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
-               if (solve_map (future_map, copy, bbt)) {
-                       solve_map (_metrics, ms, bbt);
+               if (solve_map_bbt (future_map, copy, bbt)) {
+                       solve_map_bbt (_metrics, ms, bbt);
                        recompute_tempos (_metrics);
                }
        }
@@ -2630,7 +2459,7 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
                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;
@@ -2733,12 +2562,6 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
                                new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
                        }
 
-                       const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
-                       if (diff > -0.1 && diff  < 0.1) {
-                               new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
-                                                                       / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
-                       }
-
                } else if (prev_t->c_func() > 0.0) {
                        if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
                                new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
@@ -2746,20 +2569,20 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
                                /* prev_to_prev_t is irrelevant */
                                new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
                        }
+               }
 
-                       /* limits - a bit clunky, but meh */
-                       const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
-                       if (diff > -0.1 && diff  < 0.1) {
-                               new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
-                                                                       / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
-                       }
+               /* limits - a bit clunky, but meh */
+               const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
+               if (diff > -1.0 && diff  < 1.0) {
+                       new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+                                                               / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
                }
 
                prev_t->set_beats_per_minute (new_bpm);
                recompute_tempos (future_map);
                recompute_meters (future_map);
 
-               if (check_solved (future_map, true)) {
+               if (check_solved (future_map)) {
 
                        prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
                        prev_t->set_beats_per_minute (new_bpm);
@@ -2783,16 +2606,21 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
 }
 
 void
-TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame)
-{
+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;
-       TempoSection* ts = 0;
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 
-               ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
-
                if (!ts) {
                        return;
                }
@@ -2805,31 +2633,38 @@ TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame
                        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 (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
-                       /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
-                       contribution = prev_to_prev_t->beats_per_minute() / (prev_to_prev_t->beats_per_minute() + prev_t->beats_per_minute());
+               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);
-
-               const double start_tempo = prev_t->tempo_at_frame (frame, _frame_rate);
-               const double end_tempo = prev_t->tempo_at_frame (frame + prev_t_frame_contribution, _frame_rate);
-               const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
-               const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+               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_t->frame())
-                                                                               / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
+
+                                       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 */
@@ -2856,21 +2691,29 @@ TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame
                                }
                        }
                } else {
-                       const double end_minute = (((frame + prev_t_frame_contribution) - prev_t->frame()) / (double) _frame_rate) / 60.0;
 
-                       new_bpm = (((start_pulse  - prev_t->pulse()) * prev_t->c_func())
-                                  / (exp (end_minute * prev_t->c_func()) - 1)) * (double) prev_t->note_type();
+                       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, true)) {
-
-                       prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
-                       prev_t->set_beats_per_minute (new_bpm);
+               if (check_solved (future_map)) {
+                       ts->set_beats_per_minute (new_bpm);
                        recompute_tempos (_metrics);
                        recompute_meters (_metrics);
                }