Tempo ramps - more click fixing.
[ardour.git] / libs / ardour / tempo.cc
index b9385c76584166f63ff4df39ab31cf5f240edbb1..8a481c9567e1d67c7883a8b3b8bfb2fe9e7ba6a3 100644 (file)
@@ -84,6 +84,8 @@ TempoSection::TempoSection (const XMLNode& node)
        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 +94,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,7 +155,11 @@ 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())));
        }
@@ -198,7 +202,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 +217,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 +229,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 +243,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 +257,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 +272,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();
        }
 
@@ -455,14 +458,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 +472,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 +521,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,26 +573,38 @@ MeterSection::get_state() const
 /*
   Tempo Map Overview
 
-  We have tempos, which are nice to think of in whole pulses per minute,
-  and meters which divide tempo pulses into bars (via divisions_per_bar)
-  and beats (via note_divisor).
-  Tempos and meters may be locked to audio or music.
-  Because the notion of a beat cannot be determined without both tempo and meter, the first tempo
-  and first meter are special. they must move together, and must be locked to audio.
+  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.
+  Note that Tempo::beats_per_minute() has nothing to do with musical beats.
+  It should rather be thought of as tempo note divisions per minute.
+
+  TempoSections, which are nice to think of in whole pulses per minute,
+  and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
+  and beats (via note_divisor) are used to form a tempo map.
+  TempoSections and MeterSections may be locked to either audio or music (position lock style).
+  We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
+  We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
+
+  Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
+
+  The first tempo and first meter are special. they must move together, and must be locked to audio.
   Audio locked tempos which lie before the first meter are made inactive.
   They will be re-activated if the first meter is again placed before them.
 
   Both tempos and meters have a pulse position and a frame position.
-  Meters also have a beat position, which is 0.0 for the first meter.
-  A tempo locked to music is locked to pulses.
+  Meters also have a beat position, which is always 0.0 for the first meter.
+
+  A tempo locked to music is locked to musical pulses.
   A meter locked to music is locked to beats.
+
   Recomputing the tempo map is the process where the 'missing' position
-  (tempo pulse or meter pulse & beat in the case of AudioTime and frame for MusicTime) is calculated
-  based on the lock preference (position_lock_style).
+  (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
 
   It is important to keep the _metrics in an order that makes sense.
-  Because ramped MusicTime and AudioTime tempos can interact with each other
-  and cause reordering, care must be taken to keep _metrics in a solved state.
+  Because ramped MusicTime and AudioTime tempos can interact with each other,
+  reordering is frequent. Care must be taken to keep _metrics in a solved state.
   Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
 */
 struct MetricSectionSorter {
@@ -606,8 +624,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 ((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());
 
        t->set_movable (false);
        m->set_movable (false);
@@ -751,6 +769,10 @@ 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 {
                                        _metrics.erase (i);
@@ -764,7 +786,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()) {
 
@@ -774,11 +796,10 @@ 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 {
                                        _metrics.erase (i);
-
                                }
 
                                break;
@@ -802,7 +823,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;
                                        }
                                }
@@ -871,51 +892,61 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
        PropertyChanged (PropertyChange ());
 }
 
-void
+TempoSection*
 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
 {
+       TempoSection* ts = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_tempo_locked (tempo, pulse, true, type);
+               ts = add_tempo_locked (tempo, pulse, true, type);
        }
 
        PropertyChanged (PropertyChange ());
+
+       return ts;
 }
 
-void
+TempoSection*
 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
 {
+       TempoSection* ts = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_tempo_locked (tempo, frame, true, type);
+               ts = add_tempo_locked (tempo, frame, true, type);
        }
 
 
        PropertyChanged (PropertyChange ());
+
+       return ts;
 }
 
-void
+TempoSection*
 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
 {
-       TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
+       TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
 
-       do_insert (ts);
+       do_insert (t);
 
        if (recompute) {
-               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
+               solve_map (_metrics, t, t->pulse());
        }
+
+       return t;
 }
 
-void
+TempoSection*
 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);
+       TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
 
-       do_insert (ts);
+       do_insert (t);
 
        if (recompute) {
-               solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
+               solve_map (_metrics, t, t->frame());
        }
+
+       return t;
 }
 
 void
@@ -923,18 +954,17 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               MeterSection& first (first_meter());
 
                if (ms.movable()) {
                        remove_meter_locked (ms);
                        add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
                } else {
-                       const PositionLockStyle pl = ms.position_lock_style();
+                       MeterSection& first (first_meter());
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
-                       first.set_position_lock_style (pl);
-                       recompute_map (_metrics);
+                       first.set_position_lock_style (AudioTime);
                }
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -948,6 +978,7 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const frame
 
                const double beat = ms.beat();
                const BBT_Time bbt = ms.bbt();
+
                if (ms.movable()) {
                        remove_meter_locked (ms);
                        add_meter_locked (meter, frame, beat, bbt, true);
@@ -964,20 +995,20 @@ 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
+MeterSection*
 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
 {
+       MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, beat, where, true);
+               m = add_meter_locked (meter, beat, where, true);
        }
 
 
@@ -988,14 +1019,17 @@ TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& whe
 #endif
 
        PropertyChanged (PropertyChange ());
+
+       return m;
 }
 
-void
+MeterSection*
 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
 {
+       MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, frame, beat, where, true);
+               m = add_meter_locked (meter, frame, beat, where, true);
        }
 
 
@@ -1006,9 +1040,11 @@ TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double&
 #endif
 
        PropertyChanged (PropertyChange ());
+
+       return m;
 }
 
-void
+MeterSection*
 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
@@ -1024,21 +1060,20 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, boo
        }
        /* new meters *always* start on a beat. */
        where.ticks = 0;
-       double pulse = pulse_at_beat_locked (_metrics, beat);
-
+       const double pulse = pulse_at_beat_locked (_metrics, beat);
        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);
+               solve_map (_metrics, new_meter, pulse);
        }
 
+       return new_meter;
 }
 
-void
+MeterSection*
 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);
@@ -1047,343 +1082,130 @@ TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, T
        do_insert (new_meter);
 
        if (recompute) {
-               solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
+               solve_map (_metrics, new_meter, frame);
        }
 
+       return new_meter;
 }
 
-/**
-* This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
-* taking any possible reordering as a consequence of this into account.
-* @param section - the section to be altered
-* @param bpm - the new Tempo
-* @param bbt - the bbt where the altered tempo will fall
-* @return returns - the position in frames where the new tempo section will lie.
-*/
-framepos_t
-TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
+void
+TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics future_map;
-       framepos_t ret = 0;
-       TempoSection* new_section = copy_metrics_and_point (future_map, section);
-
-       double const beat = bbt_to_beats_locked (future_map, bbt);
-       if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
-               ret = new_section->frame();
-       } else {
-               ret = frame_at_beat_locked (_metrics, beat);
-       }
+       Tempo newtempo (beats_per_minute, note_type);
+       TempoSection* t;
 
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       {
+                               Glib::Threads::RWLock::WriterLock lm (lock);
+                               *((Tempo*) t) = newtempo;
+                               recompute_map (_metrics);
+                       }
+                       PropertyChanged (PropertyChange ());
+                       break;
+               }
        }
-       return ret;
 }
 
-double
-TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
+void
+TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics future_map;
-       double ret = 0.0;
-       TempoSection* new_section = copy_metrics_and_point (future_map, section);
+       Tempo newtempo (beats_per_minute, note_type);
 
-       if (solve_map (future_map, new_section, bpm, frame)) {
-               ret = new_section->pulse();
-       } else {
-               ret = pulse_at_frame_locked (_metrics, frame);
-       }
+       TempoSection* prev;
+       TempoSection* first;
+       Metrics::iterator i;
 
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
-       }
-       return ret;
-}
+       /* find the TempoSection immediately preceding "where"
+        */
 
-void
-TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
-{
-       Metrics future_map;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
-               if (solve_map (future_map, new_section, bpm, frame)) {
-                       solve_map (_metrics, ts, bpm, frame);
-               }
-       }
+       for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
 
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
-       }
+               if ((*i)->frame() > where) {
+                       break;
+               }
 
-       MetricPositionChanged (); // Emit Signal
-}
+               TempoSection* t;
 
-void
-TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double& beat)
-{
-       Metrics future_map;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
-               if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
-                       solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
+               if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!first) {
+                               first = t;
+                       }
+                       prev = t;
                }
        }
 
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
+       if (!prev) {
+               if (!first) {
+                       error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
+                       return;
+               }
+
+               prev = first;
        }
 
-       MetricPositionChanged (); // Emit Signal
-}
+       /* reset */
 
-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);
+               /* cannot move the first tempo section */
+               *((Tempo*)prev) = newtempo;
+               recompute_map (_metrics);
        }
 
-       MetricPositionChanged (); // Emit Signal
+       PropertyChanged (PropertyChange ());
 }
 
-void
-TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double&  beat)
+const MeterSection&
+TempoMap::first_meter () const
 {
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
+       const MeterSection *m = 0;
+
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
+                       return *m;
+               }
        }
 
-       MetricPositionChanged (); // Emit Signal
+       fatal << _("programming error: no meter section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
+       return *m;
 }
 
-bool
-TempoMap::gui_change_tempo (TempoSection* ts,  const Tempo& bpm)
+MeterSection&
+TempoMap::first_meter ()
 {
-       Metrics future_map;
-       bool can_solve = false;
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
-               new_section->set_beats_per_minute (bpm.beats_per_minute());
-               recompute_tempos (future_map);
+       MeterSection *m = 0;
 
-               if (check_solved (future_map, true)) {
-                       ts->set_beats_per_minute (bpm.beats_per_minute());
-                       recompute_map (_metrics);
-                       can_solve = true;
+       /* CALLER MUST HOLD LOCK */
+
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       return *m;
                }
        }
 
-       Metrics::const_iterator d = future_map.begin();
-       while (d != future_map.end()) {
-               delete (*d);
-               ++d;
-       }
-       if (can_solve) {
-               MetricPositionChanged (); // Emit Signal
-       }
-       return can_solve;
+       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
+       return *m;
 }
 
-TempoSection*
-TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
+const TempoSection&
+TempoMap::first_tempo () const
 {
-       TempoSection* t;
-       TempoSection* ret = 0;
-       MeterSection* m;
+       const TempoSection *t = 0;
 
-       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (t == section) {
-                               if (t->position_lock_style() == MusicTime) {
-                                       ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
-                               } else {
-                                       ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
-                               }
-                               ret->set_active (t->active());
-                               ret->set_movable (t->movable());
-                               copy.push_back (ret);
-                               continue;
-                       }
-                       TempoSection* cp = 0;
-                       if (t->position_lock_style() == MusicTime) {
-                               cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
-                       } else {
-                               cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
-                       }
-                       cp->set_active (t->active());
-                       cp->set_movable (t->movable());
-                       copy.push_back (cp);
-               }
-               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       MeterSection* cp = 0;
-                       if (m->position_lock_style() == MusicTime) {
-                               cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
-                       } else {
-                               cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
-                       }
-                       cp->set_movable (m->movable());
-                       copy.push_back (cp);
-               }
-       }
-       //recompute_map (copy);
-       return ret;
-}
-
-bool
-TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
-{
-       Metrics copy;
-       TempoSection* new_section = 0;
-
-       {
-               Glib::Threads::RWLock::ReaderLock lm (lock);
-               new_section = copy_metrics_and_point (copy, ts);
-       }
-
-       double const beat = bbt_to_beats_locked (copy, bbt);
-       bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
-
-       Metrics::const_iterator d = copy.begin();
-       while (d != copy.end()) {
-               delete (*d);
-               ++d;
-       }
-
-       return ret;
-}
-
-void
-TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
-{
-       Tempo newtempo (beats_per_minute, note_type);
-       TempoSection* t;
-
-       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (!t->active()) {
-                               continue;
-                       }
-                       {
-                               Glib::Threads::RWLock::WriterLock lm (lock);
-                               *((Tempo*) t) = newtempo;
-                               recompute_map (_metrics);
-                       }
-                       PropertyChanged (PropertyChange ());
-                       break;
-               }
-       }
-}
-
-void
-TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
-{
-       Tempo newtempo (beats_per_minute, note_type);
-
-       TempoSection* prev;
-       TempoSection* first;
-       Metrics::iterator i;
-
-       /* find the TempoSection immediately preceding "where"
-        */
-
-       for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
-
-               if ((*i)->frame() > where) {
-                       break;
-               }
-
-               TempoSection* t;
-
-               if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                       if (!t->active()) {
-                               continue;
-                       }
-                       if (!first) {
-                               first = t;
-                       }
-                       prev = t;
-               }
-       }
-
-       if (!prev) {
-               if (!first) {
-                       error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
-                       return;
-               }
-
-               prev = first;
-       }
-
-       /* reset */
-
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               /* cannot move the first tempo section */
-               *((Tempo*)prev) = newtempo;
-               recompute_map (_metrics);
-       }
-
-       PropertyChanged (PropertyChange ());
-}
-
-const MeterSection&
-TempoMap::first_meter () const
-{
-       const MeterSection *m = 0;
-
-       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-               if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
-                       return *m;
-               }
-       }
-
-       fatal << _("programming error: no meter section in tempo map!") << endmsg;
-       abort(); /*NOTREACHED*/
-       return *m;
-}
-
-MeterSection&
-TempoMap::first_meter ()
-{
-       MeterSection *m = 0;
-
-       /* CALLER MUST HOLD LOCK */
-
-       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       return *m;
-               }
-       }
-
-       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
-       abort(); /*NOTREACHED*/
-       return *m;
-}
-
-const TempoSection&
-TempoMap::first_tempo () const
-{
-       const TempoSection *t = 0;
-
-       /* CALLER MUST HOLD LOCK */
-
-       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-               if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
-                       if (!t->active()) {
+       /* CALLER MUST HOLD LOCK */
+
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
+                       if (!t->active()) {
                                continue;
                        }
                        if (!t->movable()) {
@@ -1420,7 +1242,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;
@@ -1429,21 +1251,26 @@ TempoMap::recompute_tempos (Metrics& metrics)
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_ts) {
+                       if (!t->movable()) {
+                               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));
+                                       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 */
@@ -1457,26 +1284,38 @@ TempoMap::recompute_meters (Metrics& metrics)
                if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
                        if (meter->position_lock_style() == AudioTime) {
                                double pulse = 0.0;
-                               if (prev_m) {
-                                       double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
-
-                                       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;
+                               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());
+                                       const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
+                                       if (floor_beats + prev_m->beat() < meter->beat()) {
+                                               /* tempo change caused a change in beat (bar). */
+                                               b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
+                                               const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
+                                               pulse = true_pulse - pulse_off;
+                                       } else {
+                                               b_bbt = make_pair (meter->beat(), meter->bbt());
+                                               pulse = pulse_at_frame_locked (metrics, meter->frame());
+                                       }
+                               } else {
+                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                }
+                               meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
-
-                               if (!meter->movable()) {
-                                       pair<double, BBT_Time> bt = make_pair (0.0, BBT_Time (1, 1, 0));
-                                       meter->set_beat (bt);
-                               }
                        } else {
                                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();
+                                       pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
+                                       new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
                                } else {
+                                       /* shouldn't happen - the first is audio-locked */
                                        pulse = pulse_at_beat_locked (metrics, meter->beat());
+                                       new_beat = make_pair (pulse, meter->bbt());
                                }
+
+                               meter->set_beat (new_beat);
                                meter->set_frame (frame_at_pulse_locked (metrics, pulse));
                                meter->set_pulse (pulse);
                        }
@@ -1484,6 +1323,7 @@ TempoMap::recompute_meters (Metrics& metrics)
                        prev_m = meter;
                }
        }
+       //dump (_metrics, std::cerr;
 }
 
 void
@@ -1512,63 +1352,6 @@ TempoMap::recompute_map (Metrics& metrics, framepos_t end)
        recompute_meters (metrics);
 }
 
-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
-{
-       MeterSection* prev_ms = 0;
-       double accumulated_beats = 0.0;
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms && m->beat() > beat) {
-                               break;
-                       }
-                       accumulated_beats = m->beat();
-                       prev_ms = m;
-               }
-
-       }
-       double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
-       return ret;
-}
-
-double
-TempoMap::beat_at_pulse (const double& pulse) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       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;
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms && m->pulse() > pulse) {
-                               break;
-                       }
-                       accumulated_beats = m->beat();
-                       prev_ms = m;
-               }
-       }
-
-       double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
-
-       return beats_in_section + accumulated_beats;
-}
-
 TempoMetric
 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 {
@@ -1628,29 +1411,162 @@ TempoMap::metric_at (BBT_Time bbt) const
        return m;
 }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
+double
+TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
+       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;
+               }
 
-       if (frame < 0) {
-               bbt.bars = 1;
-               bbt.beats = 1;
-               bbt.ticks = 0;
-               warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
-               return;
        }
+       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
+{
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       double const beat = beat_at_frame_locked (_metrics, frame);
+       return pulse_at_beat_locked (_metrics, beat);
+}
 
-       bbt = beats_to_bbt_locked (_metrics, beat);
+double
+TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       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->pulse() > pulse) {
+                               if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
+                                       break;
+                               }
+                       }
+                       prev_m = m;
+               }
+       }
+
+       double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
+
+       return beats_in_section + prev_m->beat();
 }
 
 double
-TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
+TempoMap::beat_at_pulse (const double& pulse) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_pulse_locked (_metrics, pulse);
+}
 
-       return bbt_to_beats_locked (_metrics, bbt);
+double
+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();
+}
+
+double
+TempoMap::pulse_at_frame (const framecnt_t& frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return pulse_at_frame_locked (_metrics, frame);
+}
+
+framecnt_t
+TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       /* HOLD THE READER LOCK */
+
+       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 (!t->active()) {
+                               continue;
+                       }
+                       if (prev_t && t->pulse() > pulse) {
+                               return prev_t->frame_at_pulse (pulse, _frame_rate);
+                       }
+
+                       prev_t = t;
+               }
+       }
+       /* must be treated as constant, irrespective of _type */
+       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_t->frame();
+
+       return ret;
+}
+
+framecnt_t
+TempoMap::frame_at_pulse (const double& pulse) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return frame_at_pulse_locked (_metrics, pulse);
+}
+
+double
+TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+{
+       const MeterSection& prev_m = meter_section_at_locked (metrics, frame);
+       const TempoSection& ts = tempo_section_at_locked (metrics, frame);
+       if (frame < prev_m.frame()) {
+               return 0.0;
+       }
+       return prev_m.beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m.pulse()) * prev_m.note_divisor();
+}
+
+double
+TempoMap::beat_at_frame (const framecnt_t& frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beat_at_frame_locked (_metrics, frame);
+}
+
+framecnt_t
+TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+{
+       const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
+       return frame;
+}
+
+framecnt_t
+TempoMap::frame_at_beat (const double& beat) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return frame_at_beat_locked (_metrics, beat);
 }
 
 double
@@ -1658,78 +1574,65 @@ TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time&
 {
        /* 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.
        */
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       double bars_to_m = 0.0;
-                       if (prev_ms) {
-                               bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
-                               if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
+                       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;
                }
        }
 
-       double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
-       double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
-       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+       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;
 }
 
-Timecode::BBT_Time
-TempoMap::beats_to_bbt (const double& beats)
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return beats_to_bbt_locked (_metrics, beats);
+       return bbt_to_beats_locked (_metrics, bbt);
 }
 
 Timecode::BBT_Time
 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
 {
        /* CALLER HOLDS READ LOCK */
-       MeterSection* prev_ms = 0;
+       MeterSection* prev_m = 0;
        const double beats = (b < 0.0) ? 0.0 : b;
-       uint32_t accumulated_bars = 0;
-       double accumulated_beats = 0.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 beats_to_m = m->beat() - prev_ms->beat();
-                               if (accumulated_beats + beats_to_m > beats) {
+                       if (prev_m) {
+                               if (m->beat() > beats) {
                                        /* this is the meter after the one our beat is on*/
                                        break;
                                }
-
-                               /* 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;
                }
        }
 
-       double const beats_in_ms = beats - accumulated_beats;
-       uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
-       uint32_t const total_bars = bars_in_ms + accumulated_bars;
-       double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
-       double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
+       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;
 
@@ -1746,7 +1649,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;
        }
@@ -1754,39 +1657,41 @@ 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;
                }
        }
-       double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
-       uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
-       uint32_t const total_bars = bars_in_ms + accumulated_bars;
-       double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
-       double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
+
+       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;
 
@@ -1803,7 +1708,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;
        }
@@ -1811,144 +1716,30 @@ TempoMap::pulse_to_bbt (const double& pulse)
        return ret;
 }
 
-double
-TempoMap::beat_at_frame (const framecnt_t& frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_frame_locked (_metrics, frame);
-}
-
-double
-TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
-{
-       //framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
-       double const pulse = pulse_at_frame_locked (metrics, frame);
-
-       return beat_at_pulse_locked (metrics, pulse);
-}
-
-double
-TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
-{
-       /* HOLD (at least) THE READER LOCK */
-       TempoSection* prev_ts = 0;
-       double accumulated_pulses = 0.0;
-
-       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 */
-                               double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
-                               return ret;
-                       }
-                       accumulated_pulses = t->pulse();
-                       prev_ts = t;
-               }
-       }
-
-       /* treated as constant for this ts */
-       double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
-
-       return pulses_in_section + accumulated_pulses;
-}
-
-framecnt_t
-TempoMap::frame_at_beat (const double& beat) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_beat_locked (_metrics, beat);
-}
-
-framecnt_t
-TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
-{
-       framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
-       //frameoffset_t const frame_off = frame_offset_at (metrics, frame);
-       return frame;
-}
-
-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;
-
-       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->pulse() > pulse) {
-                               return prev_ts->frame_at_pulse (pulse, _frame_rate);
-                       }
-
-                       accumulated_pulses = t->pulse();
-                       prev_ts = t;
-               }
-       }
-       /* must be treated as constant, irrespective of _type */
-       double const pulses_in_section = pulse - accumulated_pulses;
-       double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
-
-       framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
-
-       return ret;
-}
-
-double
-TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
+void
+TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
 {
-       MeterSection* prev_m = 0;
-       double beat_off = first_meter().pulse();
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m = 0;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_m) {
-                               if (m->beat() > beat) {
-                                       break;
-                               }
 
-                               if (m->position_lock_style() == AudioTime) {
-                                       beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
-                               }
-                       }
-                       prev_m = m;
-               }
+       if (frame < 0) {
+               bbt.bars = 1;
+               bbt.beats = 1;
+               bbt.ticks = 0;
+               warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
+               return;
        }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       const double beat = beat_at_frame_locked (_metrics, frame);
 
-       return beat_off;
+       bbt = beats_to_bbt_locked (_metrics, beat);
 }
 
-frameoffset_t
-TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
+framepos_t
+TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
 {
-       frameoffset_t frame_off = 0;
-       MeterSection* prev_m = 0;
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m = 0;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (m->frame() > frame) {
-                               break;
-                       }
-                       if (prev_m && m->position_lock_style() == AudioTime) {
-                               const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
-                               frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
-                       }
-                       prev_m = m;
-               }
-       }
+       /* HOLD THE READER LOCK */
 
-       return frame_off;
+       const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
+       return ret;
 }
 
 framepos_t
@@ -1963,25 +1754,14 @@ TempoMap::frame_time (const BBT_Time& bbt)
                throw std::logic_error ("beats are counted from one");
        }
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       double const beat = bbt_to_beats_locked (_metrics, bbt);
-       framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
-       return frame;
-}
 
-framepos_t
-TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
-{
-       /* HOLD THE READER LOCK */
-
-       framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
-
-       return ret;
+       return frame_time_locked (_metrics, bbt);
 }
 
 bool
 TempoMap::check_solved (Metrics& metrics, bool by_frame)
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
@@ -1989,21 +1769,21 @@ TempoMap::check_solved (Metrics& metrics, bool by_frame)
                        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 ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
                                        return false;
                                }
 
-                               if (t->frame() == prev_ts->frame()) {
+                               if (t->frame() == prev_t->frame()) {
                                        return false;
                                }
 
-                               /* precision check ensures pulses and frames align independent of lock style.*/
-                               if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
+                               /* precision check ensures pulses and frames align.*/
+                               if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
                                        return false;
                                }
                        }
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
 
@@ -2034,9 +1814,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 (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;
 
@@ -2051,9 +1831,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) {
@@ -2063,20 +1843,20 @@ 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));
+                                       t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
                                }
                        }
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
 
@@ -2109,11 +1889,12 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
        } else {
                recompute_tempos (imaginary);
        }
+
        if (check_solved (imaginary, true)) {
                recompute_meters (imaginary);
                return true;
        }
-
+#if (0)
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
        if (section->position_lock_style() == MusicTime) {
@@ -2124,19 +1905,21 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
        } else {
                recompute_tempos (imaginary);
        }
+
        if (check_solved (imaginary, true)) {
                recompute_meters (imaginary);
                return true;
        }
+#endif
        //dump (imaginary, std::cerr);
 
        return false;
 }
 
 bool
-TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
 {
-       TempoSection* prev_ts = 0;
+       TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
 
        section->set_pulse (pulse);
@@ -2147,20 +1930,25 @@ 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));
+                                       t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
                                }
                        }
-                       prev_ts = t;
+                       prev_t = t;
                }
        }
        if (section_prev) {
@@ -2176,192 +1964,451 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm
        } else {
                recompute_tempos (imaginary);
        }
-       if (check_solved (imaginary, false)) {
-               recompute_meters (imaginary);
-               return true;
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+
+       MetricSectionSorter cmp;
+       imaginary.sort (cmp);
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+#if (0)
+       MetricSectionFrameSorter fcmp;
+       imaginary.sort (fcmp);
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+#endif
+       //dump (imaginary, std::cerr);
+
+       return false;
+}
+
+void
+TempoMap::solve_map (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);
+       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
+               return;
+       }
+       MeterSection* prev_m = 0;
+
+       if (!section->movable()) {
+               /* lock the first tempo to our first meter */
+               if (!set_active_tempos (imaginary, frame)) {
+                       return;
+               }
+               TempoSection* first_t = &first_tempo();
+               Metrics future_map;
+               TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
+
+               new_section->set_frame (frame);
+               new_section->set_pulse (0.0);
+               new_section->set_active (true);
+
+               if (solve_map (future_map, new_section, frame)) {
+                       first_t->set_frame (frame);
+                       first_t->set_pulse (0.0);
+                       first_t->set_active (true);
+                       solve_map (imaginary, first_t, frame);
+               } else {
+                       return;
+               }
+       }
+
+       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 first beat
+                                 and use the delta to find the meter's pulse.
+                               */
+                               double new_pulse = 0.0;
+                               pair<double, BBT_Time> b_bbt;
+
+                               if (section->movable()) {
+                                       const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
+                                       const double floor_beats = beats - fmod (beats,  prev_m->divisions_per_bar());
+                                       if (floor_beats + prev_m->beat() < section->beat()) {
+                                               /* disallow position change if it will alter out 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.
+                                               */
+                                               return;
+                                       }
+                                       b_bbt = make_pair (section->beat(), section->bbt());
+                                       new_pulse = pulse_at_frame_locked (imaginary, frame);
+                               } else {
+                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                               }
+                               section->set_frame (frame);
+                               section->set_beat (b_bbt);
+                               section->set_pulse (new_pulse);
+                               prev_m = m;
+
+                               continue;
+                       }
+                       if (prev_m) {
+                               double new_pulse = 0.0;
+                               if (m->position_lock_style() == MusicTime) {
+                                       new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
+                                       m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
+                                       if (m->frame() > section->frame()) {
+                                               /* moving 'section' will affect later meters' beat (but not bbt).*/
+                                               pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
+                                               m->set_beat (new_beat);
+                                       }
+                               } else {
+                                       pair<double, BBT_Time> b_bbt;
+                                       if (m->movable()) {
+                                               const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
+                                               const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
+                                               b_bbt = make_pair (floor_beats + prev_m->beat()
+                                                                  , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
+                                               const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
+                                               new_pulse = true_pulse - pulse_off;
+                                       } else {
+                                               b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
+                                               new_pulse = 0.0;
+                                       }
+                                       m->set_beat (b_bbt);
+                               }
+                               m->set_pulse (new_pulse);
+                       }
+                       prev_m = m;
+               }
+       }
+
+       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);
+       }
+       //dump (imaginary, std::cerr);
+}
+
+void
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
+{
+       MeterSection* prev_m = 0;
+       MeterSection* section_prev = 0;
+
+       section->set_pulse (pulse);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (m == section){
+                               section_prev = prev_m;
+                               continue;
+                       }
+                       if (prev_m) {
+                               double new_pulse = 0.0;
+                               if (m->position_lock_style() == MusicTime) {
+                                       new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
+                                       m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
+
+                                       if (new_pulse > section->pulse()) {
+                                               /* moving 'section' will affect later meters' beat (but not bbt).*/
+                                               pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
+                                               m->set_beat (new_beat);
+                                       }
+                               } else {
+                                       pair<double, BBT_Time> b_bbt;
+                                       if (m->movable()) {
+                                               const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
+                                               const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
+                                               b_bbt = make_pair (floor_beats + prev_m->beat()
+                                                                  , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
+                                               const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
+                                               new_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 (new_pulse);
+                       }
+                       prev_m = m;
+               }
+       }
+
+       if (section_prev) {
+               const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
+               const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
+               pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
+               section->set_beat (b_bbt);
+               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+       }
+
+       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);
+       }
+}
+
+/** places a copy of _metrics into copy and returns a pointer
+ *  to section's equivalent in copy.
+ */
+TempoSection*
+TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
+{
+       TempoSection* t;
+       MeterSection* m;
+       TempoSection* ret = 0;
+
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (t == section) {
+                               if (t->position_lock_style() == MusicTime) {
+                                       ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
+                                       ret->set_frame (t->frame());
+                               } else {
+                                       ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
+                                       ret->set_pulse (t->pulse());
+                               }
+                               ret->set_active (t->active());
+                               ret->set_movable (t->movable());
+                               copy.push_back (ret);
+                               continue;
+                       }
+                       TempoSection* cp = 0;
+                       if (t->position_lock_style() == MusicTime) {
+                               cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
+                               cp->set_frame (t->frame());
+                       } else {
+                               cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
+                               cp->set_pulse (t->pulse());
+                       }
+                       cp->set_active (t->active());
+                       cp->set_movable (t->movable());
+                       copy.push_back (cp);
+               }
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       MeterSection* cp = 0;
+                       if (m->position_lock_style() == MusicTime) {
+                               cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
+                               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());
+                       }
+                       cp->set_movable (m->movable());
+                       copy.push_back (cp);
+               }
+       }
+
+       return ret;
+}
+
+bool
+TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
+{
+       Metrics copy;
+       TempoSection* new_section = 0;
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               new_section = copy_metrics_and_point (copy, ts);
+       }
+
+       double const beat = bbt_to_beats_locked (copy, bbt);
+       bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
+
+       Metrics::const_iterator d = copy.begin();
+       while (d != copy.end()) {
+               delete (*d);
+               ++d;
        }
 
-       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);
+       return ret;
+}
+
+/**
+* This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
+* taking any possible reordering as a consequence of this into account.
+* @param section - the section to be altered
+* @param bpm - the new Tempo
+* @param bbt - the bbt where the altered tempo will fall
+* @return returns - the position in frames where the new tempo section will lie.
+*/
+framepos_t
+TempoMap::predict_tempo_frame (TempoSection* section, const 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, pulse_at_beat_locked (future_map, beat))) {
+               ret = new_section->frame();
        } else {
-               recompute_tempos (imaginary);
+               ret = frame_at_beat_locked (_metrics, beat);
        }
 
-       if (check_solved (imaginary, false)) {
-               recompute_meters (imaginary);
-               return true;
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
        }
+       return ret;
+}
 
-       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);
+double
+TempoMap::predict_tempo_pulse (TempoSection* section, 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, frame)) {
+               ret = new_section->pulse();
        } else {
-               recompute_tempos (imaginary);
+               ret = pulse_at_frame_locked (_metrics, frame);
        }
 
-       if (check_solved (imaginary, false)) {
-               recompute_meters (imaginary);
-               return true;
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
        }
-
-       //dump (imaginary, std::cerr);
-
-       return false;
+       return ret;
 }
 
 void
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
+TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
 {
-       MeterSection* prev_ms = 0;
-
-       if (!section->movable()) {
-               /* lock the first tempo to our first meter */
-               if (!set_active_tempos (imaginary, frame)) {
-                       return;
+       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, frame)) {
+                       solve_map (_metrics, ts, frame);
                }
-               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);
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
 
-               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;
+       MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
+{
+       Metrics future_map;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
+               if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
+                       solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
                }
        }
 
-       double accumulated_beats = 0.0;
-       uint32_t accumulated_bars = 0;
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
 
-       section->set_frame (frame);
+       MetricPositionChanged (); // Emit Signal
+}
 
-       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms) {
-                               const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
-                               accumulated_beats += beats_in_m;
-                               accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
-                       }
-                       if (m == section){
-                               /*
-                                 here we define the pulse for this frame.
-                                 we're going to set it 'incorrectly' to the next integer and use this 'error'
-                                 as an offset to the map as far as users of the public methods are concerned.
-                                 (meters should go on absolute pulses to keep us sane)
-                               */
-                               pair<double, BBT_Time> b_bbt;
-                               if (m->movable()) {
-                                       double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
-                                       b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
-                                       m->set_beat (b_bbt);
-                                       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());
-                                       m->set_pulse (true_pulse - pulse_off);
-                               } else {
-                                       b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
-                                       m->set_pulse (0.0);
-                                       m->set_beat (b_bbt);
-                               }
-                               //m->set_beat (b_bbt);
-                               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);
-                               } else {
-                                       if (!m->movable()) {
-                                               pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
-                                               m->set_beat (b_bbt);
-                                       }
-                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
-                                       m->set_pulse (pulse);
-                               }
-                       }
-                       prev_ms = m;
-               }
+void
+TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, frame);
        }
 
-       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);
+       MetricPositionChanged (); // Emit Signal
 }
 
 void
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
+TempoMap::gui_move_meter (MeterSection* ms, const double&  beat)
 {
-       MeterSection* prev_ms = 0;
-       double accumulated_beats = 0.0;
-       uint32_t accumulated_bars = 0;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
+       }
 
-       section->set_pulse (pulse);
+       MetricPositionChanged (); // Emit Signal
+}
 
-       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms) {
-                               double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
-                               accumulated_beats += beats_in_m;
-                               accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
-                       }
-                       if (m == section){
-                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
-                               pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
-                               section->set_beat (b_bbt);
-                               prev_ms = section;
-                               continue;
-                       }
-                       if (prev_ms) {
-                               if (m->position_lock_style() == MusicTime) {
-                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
-                                       m->set_frame (frame_at_pulse_locked (imaginary, pulse));
-                                       m->set_pulse (pulse);
-                               } else {
-                                       if (!m->movable()) {
-                                               pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
-                                               m->set_beat (b_bbt);
-                                       }
-                                       const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
-                                       m->set_pulse (pulse);
-                               }
-                       }
-                       prev_ms = m;
+bool
+TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
+{
+       Metrics future_map;
+       bool can_solve = false;
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection* new_section = copy_metrics_and_point (future_map, ts);
+               new_section->set_beats_per_minute (bpm.beats_per_minute());
+               recompute_tempos (future_map);
+
+               if (check_solved (future_map, true)) {
+                       ts->set_beats_per_minute (bpm.beats_per_minute());
+                       recompute_map (_metrics);
+                       can_solve = true;
                }
        }
 
-       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);
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       if (can_solve) {
+               MetricPositionChanged (); // Emit Signal
        }
+       return can_solve;
 }
 
 framecnt_t
@@ -2369,13 +2416,11 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
-       double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-       double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
-       framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
-       framecnt_t const ret = time_at_bbt;
+       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;
 
-       return ret;
+       return frame_at_beat_locked (_metrics, total_beats);
 }
 
 framepos_t
@@ -2481,7 +2526,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                }
        }
 
-       framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+       const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
 
        return ret_frame;
 }
@@ -2542,7 +2587,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
+       const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
        BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
 
        switch (type) {
@@ -2551,22 +2596,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;
@@ -2596,19 +2641,19 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    framepos_t lower, framepos_t upper)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
-       int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
+       const int32_t upper_beat = (int32_t) floor (beat_at_frame_locked (_metrics, upper));
+       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);
-               TempoSection const tempo = tempo_section_at_locked (pos);
-               MeterSection const meter = meter_section_at_locked (pos);
-               BBT_Time const bbt = beats_to_bbt (cnt);
-               BBTPoint point = BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func());
-               points.push_back (point);
+       while (cnt <= upper_beat && 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 (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
                ++cnt;
        }
 }
@@ -2617,23 +2662,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;
                        }
 
@@ -2658,7 +2703,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;
 
@@ -2687,13 +2732,13 @@ const Tempo
 TempoMap::tempo_at (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_at_locked (frame);
+       return tempo_at_locked (_metrics, 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;
 
@@ -2703,18 +2748,18 @@ 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 */
-                               double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
-                               Tempo const 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;
                }
        }
 
-       double const ret = prev_ts->beats_per_minute();
-       Tempo const 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;
 }
@@ -2723,17 +2768,16 @@ const MeterSection&
 TempoMap::meter_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return meter_section_at_locked (frame);
+       return meter_section_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) {
@@ -2764,20 +2808,82 @@ TempoMap::meter_at (framepos_t frame) const
 const MeterSection&
 TempoMap::meter_section_at (const double& beat) const
 {
-       MeterSection* prev_ms = 0;
+       MeterSection* prev_m = 0;
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_ms && m->beat() > beat) {
+                       if (prev_m && m->beat() > beat) {
                                break;
                        }
-                       prev_ms = m;
+                       prev_m = m;
                }
 
        }
-       return *prev_ms;
+       return *prev_m;
+}
+
+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&
@@ -2844,32 +2950,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.
                */
@@ -2878,19 +2971,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;
                                        }
                                }
@@ -2912,7 +3005,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) {
 
@@ -2920,11 +3013,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;