Tempo ramps - more click fixing.
[ardour.git] / libs / ardour / tempo.cc
index da22da4bd22fa45844dd6ca73571cb44a511a296..8a481c9567e1d67c7883a8b3b8bfb2fe9e7ba6a3 100644 (file)
@@ -68,20 +68,24 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
        return frames_per_grid (tempo, sr) * _divisions_per_bar;
 }
 
-
 /***********************************************************************/
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
 TempoSection::TempoSection (const XMLNode& node)
-       : MetricSection (0.0), Tempo (TempoMap::default_tempo())
+       : MetricSection (0.0)
+       , Tempo (TempoMap::default_tempo())
+       , _c_func (0.0)
+       , _active (true)
 {
        const XMLProperty *prop;
        LocaleGuard lg;
        BBT_Time bbt;
-       double beat;
+       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,
@@ -89,21 +93,19 @@ TempoSection::TempoSection (const XMLNode& node)
                            &bbt.ticks) == 3) {
                        /* legacy session - start used to be in bbt*/
                        _legacy_bbt = bbt;
-                       beat = -1.0;
-                       set_beat (beat);
+                       pulse = -1.0;
+                       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 ("beat")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
-                       error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
-               } else {
-                       set_beat (beat);
+       if ((prop = node.property ("pulse")) != 0) {
+               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;
@@ -139,13 +141,11 @@ TempoSection::TempoSection (const XMLNode& node)
 
        set_movable (string_is_affirmative (prop->value()));
 
-       if ((prop = node.property ("bar-offset")) == 0) {
-               _bar_offset = -1.0;
+       if ((prop = node.property ("active")) == 0) {
+               warning << _("TempoSection XML node has no \"active\" property") << endmsg;
+               set_active(true);
        } else {
-               if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
-                       error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
-                       throw failed_constructor();
-               }
+               set_active (string_is_affirmative (prop->value()));
        }
 
        if ((prop = node.property ("tempo-type")) == 0) {
@@ -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())));
        }
@@ -168,114 +172,111 @@ TempoSection::get_state() const
        char buf[256];
        LocaleGuard lg;
 
-       snprintf (buf, sizeof (buf), "%f", beat());
-       root->add_property ("beat", buf);
+       snprintf (buf, sizeof (buf), "%f", pulse());
+       root->add_property ("pulse", buf);
        snprintf (buf, sizeof (buf), "%li", frame());
        root->add_property ("frame", buf);
        snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
-       // snprintf (buf, sizeof (buf), "%f", _bar_offset);
-       // root->add_property ("bar-offset", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
+       snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
+       root->add_property ("active", buf);
        root->add_property ("tempo-type", enum_2_string (_type));
        root->add_property ("lock-style", enum_2_string (position_lock_style()));
 
        return *root;
 }
 
-void
-
-TempoSection::update_bar_offset_from_bbt (const Meter& m)
-{
-       _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
-               (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
-}
-
 void
 TempoSection::set_type (Type type)
 {
        _type = type;
 }
 
-/** returns the tempo at the zero-based (relative to session) frame.
+/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
 */
 double
-TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
 
-       if (_type == Constant) {
-               return beats_per_minute();
+       if (_type == Constant || _c_func == 0.0) {
+               return pulses_per_minute();
        }
 
-       return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
+       return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
 }
 
 /** returns the zero-based frame (relative to session)
-   where the tempo occurs.
+   where the tempo in whole pulses per minute occurs in this section.
+   beat b is only used for constant tempos.
+   note that the tempo map may have multiple such values.
 */
 framepos_t
-TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
+TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
-               return 0;
+       if (_type == Constant || _c_func == 0.0) {
+               return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
        }
 
-       return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate) + frame();
+       return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
 }
-
-
-/** returns the zero-based tick (relative to session origin)
-   where the zero-based frame (relative to tempo section)
-   lies.
+/** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
 */
 double
-TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
+TempoSection::tempo_at_pulse (const double& p) const
 {
-       if (_type == Constant) {
-               return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
-       }
 
-       return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
+       if (_type == Constant || _c_func == 0.0) {
+               return pulses_per_minute();
+       }
+       double const ppm = pulse_tempo_at_pulse (p - pulse());
+       return ppm;
 }
 
-/** returns the zero-based frame (relative to session origin)
-   where the zero-based tick (relative to session)
-   falls.
+/** returns the zero-based beat (relative to session)
+   where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
+   note that the session tempo map may have multiple beats at a given tempo.
 */
-framepos_t
-TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
+double
+TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
 {
-       if (_type == Constant) {
-               return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
+       if (_type == Constant || _c_func == 0.0) {
+               double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+               return  pulses;
        }
-
-       return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
+       return pulse_at_pulse_tempo (ppm) + pulse();
 }
 
-/** returns the zero-based beat (relative to session origin)
+/** returns the zero-based pulse (relative to session origin)
    where the zero-based frame (relative to session)
    lies.
 */
 double
-TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
+TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
-       return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
+       if (_type == Constant || _c_func == 0.0) {
+               return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+       }
+
+       return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
 }
 
 /** returns the zero-based frame (relative to session start frame)
-   where the zero-based beat (relative to session start)
+   where the zero-based pulse (relative to session start)
    falls.
 */
 
 framepos_t
-TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
+TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
 {
-       return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
+       if (_type == Constant || _c_func == 0.0) {
+               return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+       }
+
+       return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
 }
 
 /*
@@ -312,6 +313,12 @@ t(b) = log((cb / T0) + 1) / c
 The time t at which Tempo T occurs is a as above:
 t(T) = log(T / T0) / c
 
+The beat at which a Tempo T occurs is:
+b(T) = (T - T0) / c
+
+The Tempo at which beat b occurs is:
+T(b) = b.c + T0
+
 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
 Our problem is that we usually don't know t.
 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
@@ -325,8 +332,7 @@ We can now store c for future time calculations.
 If the following tempo section (the one that defines c in conjunction with this one)
 is changed or moved, c is no longer valid.
 
-The private methods' position parameters are all relative to this tempo section.
-the public ones are session-relative
+The public methods are session-relative.
 
 Most of this stuff is taken from this paper:
 
@@ -341,126 +347,94 @@ https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Te
 
 */
 
-/* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
-void
-TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
+/*
+  compute this ramp's function constant using the end tempo (in whole pulses per minute)
+  and duration (pulses into global start) of some later tempo section.
+*/
+double
+TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
 {
-       double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
-       _c_func = ticks_per_minute() *  (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
+       double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
+       return pulses_per_minute() *  (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
 }
 
-/* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
+/* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
 double
-TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
 {
-       return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
+       return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
 }
 
 framecnt_t
-TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
 {
        return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
 }
 
 double
-TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
+TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
 {
        return (frame / (double) frame_rate) / 60.0;
 }
 
 /* position function */
 double
-TempoSection::a_func (double end_tpm, double c_func) const
+TempoSection::a_func (double end_bpm, double c_func) const
 {
-       return log (end_tpm / ticks_per_minute()) /  c_func;
+       return log (end_bpm / pulses_per_minute()) /  c_func;
 }
 
 /*function constant*/
 double
-TempoSection::c_func (double end_tpm, double end_time) const
+TempoSection::c_func (double end_bpm, double end_time) const
 {
-       return log (end_tpm / ticks_per_minute()) /  end_time;
+       return log (end_bpm / pulses_per_minute()) /  end_time;
 }
 
-/* tempo in tpm at time in minutes */
+/* tempo in ppm at time in minutes */
 double
-TempoSection::tick_tempo_at_time (double time) const
+TempoSection::pulse_tempo_at_time (const double& time) const
 {
-       return exp (_c_func * time) * ticks_per_minute();
+       return exp (_c_func * time) * pulses_per_minute();
 }
 
-/* time in minutes at tempo in tpm */
+/* time in minutes at tempo in ppm */
 double
-TempoSection::time_at_tick_tempo (double tick_tempo) const
+TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
 {
-       return log (tick_tempo / ticks_per_minute()) / _c_func;
+       return log (pulse_tempo / pulses_per_minute()) / _c_func;
 }
 
-/* tick at time in minutes */
+/* tick at tempo in ppm */
 double
-TempoSection::tick_at_time (double time) const
+TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
 {
-       return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
+       return (pulse_tempo - pulses_per_minute()) / _c_func;
 }
 
-/* time in minutes at tick */
+/* tempo in ppm at tick */
 double
-TempoSection::time_at_tick (double tick) const
+TempoSection::pulse_tempo_at_pulse (const double& pulse) const
 {
-       return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
+       return (pulse * _c_func) + pulses_per_minute();
 }
 
-/* beat at time in minutes */
+/* pulse at time in minutes */
 double
-TempoSection::beat_at_time (double time) const
+TempoSection::pulse_at_time (const double& time) const
 {
-       return tick_at_time (time) / BBT_Time::ticks_per_beat;
+       return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
 }
 
-/* time in munutes at beat */
+/* time in minutes at pulse */
 double
-TempoSection::time_at_beat (double beat) const
-{
-       return time_at_tick (beat * BBT_Time::ticks_per_beat);
-}
-
-void
-TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
+TempoSection::time_at_pulse (const double& pulse) const
 {
-       double new_beat;
-
-       if (_bar_offset < 0.0) {
-               /* not set yet */
-               return;
-       }
-
-       new_beat = beat();
-
-       double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
-       new_beat = ticks / BBT_Time::ticks_per_beat;
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
-                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
-
-       set_beat (new_beat);
+       return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
 }
 
 /***********************************************************************/
 
-/*
-if a meter section is position locked to frames, then it can only be at 1|1|0.
-thus we can have multiple 1|1|0s in the session tempo map.
-
-like this:
-
-BBT  1|1|0 2|1|0 3|1|0 1|1|0
-beat   0     4     8    12
-
-all other meter sections are locked to beats.
-
-the beat of a meter section is used to find its position rather than the stored bbt.
-*/
-
 const string MeterSection::xml_state_node_name = "Meter";
 
 MeterSection::MeterSection (const XMLNode& node)
@@ -471,6 +445,7 @@ MeterSection::MeterSection (const XMLNode& node)
        LocaleGuard lg;
        const XMLProperty *prop;
        BBT_Time bbt;
+       double pulse = 0.0;
        double beat = 0.0;
        framepos_t frame = 0;
        pair<double, BBT_Time> start;
@@ -483,14 +458,20 @@ MeterSection::MeterSection (const XMLNode& node)
                        error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
                } else {
                        /* legacy session - start used to be in bbt*/
-                       beat = -1.0;
+                       info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
+                       pulse = -1.0;
+               }
+       }
+
+       if ((prop = node.property ("pulse")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
+                       error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
                }
-       } else {
-               error << _("MeterSection XML node has no \"start\" property") << endmsg;
        }
+       set_pulse (pulse);
 
        if ((prop = node.property ("beat")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
                        error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
                }
        }
@@ -498,17 +479,16 @@ MeterSection::MeterSection (const XMLNode& node)
        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;
-
        set_beat (start);
 
        if ((prop = node.property ("frame")) != 0) {
@@ -541,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&
@@ -563,6 +547,8 @@ MeterSection::get_state() const
        char buf[256];
        LocaleGuard lg;
 
+       snprintf (buf, sizeof (buf), "%lf", pulse());
+       root->add_property ("pulse", buf);
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  bbt().bars,
                  bbt().beats,
@@ -584,10 +570,46 @@ MeterSection::get_state() const
 }
 
 /***********************************************************************/
+/*
+  Tempo Map Overview
+
+  Tempo can be thought of as a source of the musical pulse.
+  Meters divide that pulse into measures and beats.
+  Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
+  at any particular time.
+  Note that Tempo::beats_per_minute() has nothing to do with musical beats.
+  It should rather be thought of as tempo note divisions per minute.
+
+  TempoSections, which are nice to think of in whole pulses per minute,
+  and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
+  and beats (via note_divisor) are used to form a tempo map.
+  TempoSections and MeterSections may be locked to either audio or music (position lock style).
+  We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
+  We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
 
+  Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
+
+  The first tempo and first meter are special. they must move together, and must be locked to audio.
+  Audio locked tempos which lie before the first meter are made inactive.
+  They will be re-activated if the first meter is again placed before them.
+
+  Both tempos and meters have a pulse position and a frame position.
+  Meters also have a beat position, which is always 0.0 for the first meter.
+
+  A tempo locked to music is locked to musical pulses.
+  A meter locked to music is locked to beats.
+
+  Recomputing the tempo map is the process where the 'missing' position
+  (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
+
+  It is important to keep the _metrics in an order that makes sense.
+  Because ramped MusicTime and AudioTime tempos can interact with each other,
+  reordering is frequent. Care must be taken to keep _metrics in a solved state.
+  Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
+*/
 struct MetricSectionSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->beat() < b->beat();
+           return a->pulse() < b->pulse();
     }
 };
 
@@ -600,22 +622,18 @@ struct MetricSectionFrameSorter {
 TempoMap::TempoMap (framecnt_t fr)
 {
        _frame_rate = fr;
-       BBT_Time start;
-
-       start.bars = 1;
-       start.beats = 1;
-       start.ticks = 0;
+       BBT_Time start (1, 1, 0);
 
-       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
-       MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
+       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);
 
        /* note: frame time is correct (zero) for both of these */
 
-       metrics.push_back (t);
-       metrics.push_back (m);
+       _metrics.push_back (t);
+       _metrics.push_back (m);
 
 }
 
@@ -632,7 +650,7 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
                Glib::Threads::RWLock::WriterLock lm (lock);
                if ((removed = remove_tempo_locked (tempo))) {
                        if (complete_operation) {
-                               recompute_map (true);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -647,11 +665,11 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
 {
        Metrics::iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<TempoSection*> (*i) != 0) {
                        if (tempo.frame() == (*i)->frame()) {
                                if ((*i)->movable()) {
-                                       metrics.erase (i);
+                                       _metrics.erase (i);
                                        return true;
                                }
                        }
@@ -670,7 +688,7 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
                Glib::Threads::RWLock::WriterLock lm (lock);
                if ((removed = remove_meter_locked (tempo))) {
                        if (complete_operation) {
-                               recompute_map (true);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -685,11 +703,11 @@ TempoMap::remove_meter_locked (const MeterSection& tempo)
 {
        Metrics::iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<MeterSection*> (*i) != 0) {
                        if (tempo.frame() == (*i)->frame()) {
                                if ((*i)->movable()) {
-                                       metrics.erase (i);
+                                       _metrics.erase (i);
                                        return true;
                                }
                        }
@@ -703,23 +721,22 @@ void
 TempoMap::do_insert (MetricSection* section)
 {
        bool need_add = true;
-
        /* we only allow new meters to be inserted on beat 1 of an existing
         * measure.
         */
        MeterSection* m = 0;
        if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
-               assert (m->bbt().ticks == 0);
+               //assert (m->bbt().ticks == 0);
 
                if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
-                       pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
+                       pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
                        corrected.second.beats = 1;
                        corrected.second.ticks = 0;
-                       corrected.first = bbt_to_beats_locked (corrected.second);
+                       corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
                        warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
                                                   m->bbt(), corrected.second) << endmsg;
-                       m->set_beat (corrected);
+                       //m->set_pulse (corrected);
                }
        }
 
@@ -730,16 +747,18 @@ TempoMap::do_insert (MetricSection* section)
           guaranteed that there is only one such match.
        */
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
                TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
                TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
+               MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+               MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
 
                if (tempo && insert_tempo) {
 
                        /* Tempo sections */
-                       PositionLockStyle const ipl = insert_tempo->position_lock_style();
-                       if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame())) {
+                       bool const ipm = insert_tempo->position_lock_style() == MusicTime;
+                       if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
 
                                if (!tempo->movable()) {
 
@@ -749,21 +768,25 @@ 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);
+                                       _metrics.erase (i);
                                }
                                break;
                        }
 
-               } else if (!tempo && !insert_tempo) {
+               } else if (meter && insert_meter) {
 
                        /* Meter Sections */
-                       MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
-                       MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
-                       PositionLockStyle const ipl = insert_meter->position_lock_style();
 
-                       if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
+                       bool const ipm = insert_meter->position_lock_style() == MusicTime;
+
+                       if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
 
                                if (!meter->movable()) {
 
@@ -773,10 +796,10 @@ TempoMap::do_insert (MetricSection* section)
                                         */
 
                                        *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
+                                       (*i)->set_position_lock_style (AudioTime);
                                        need_add = false;
                                } else {
-                                       metrics.erase (i);
-
+                                       _metrics.erase (i);
                                }
 
                                break;
@@ -793,81 +816,51 @@ TempoMap::do_insert (MetricSection* section)
        if (need_add) {
                MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
                TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
-
                Metrics::iterator i;
                if (insert_meter) {
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                                MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
 
                                if (meter) {
-                                       PositionLockStyle const ipl = insert_meter->position_lock_style();
-                                       if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
+                                       bool const ipm = insert_meter->position_lock_style() == MusicTime;
+                                       if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
                                                break;
                                        }
                                }
                        }
                } else if (insert_tempo) {
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                                TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
 
                                if (tempo) {
-                                       PositionLockStyle const ipl = insert_tempo->position_lock_style();
-                                       if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
+                                       bool const ipm = insert_tempo->position_lock_style() == MusicTime;
+                                       if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
                                                break;
                                        }
                                }
                        }
                }
 
-               metrics.insert (i, section);
-               //dump (std::cerr);
+               _metrics.insert (i, section);
+               //dump (_metrics, std::cerr);
        }
 }
 
-/*
-This is for a gui who needs to know the frame of a beat if a proposed tempo section were to be placed there.
-You probably shouldn't use this unless you know what you're doing,
-as it doesn't recompute the tempo map and will make a ramp invalid intil that next happens.
-It is assumed that the next frame calculation is the last time you need this c_func before the next do_insert() and/or recompute_map().
-*/
-void
-TempoMap::replace_c_func_from_tempo_and_beat (const double& bpm, const double& beat)
-{
-       Glib::Threads::RWLock::WriterLock lm (lock);
-       TempoSection* prev_ts = 0;
-       TempoSection* t;
-
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (prev_ts) {
-                               prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
-
-                               if (beat < t->beat()) {
-                                       return;
-                               }
-                       }
-                       prev_ts = t;
-               }
-       }
-       /* there is always at least one tempo section */
-       prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
-}
-
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
-               if (ts.beat() != first.beat()) {
+               if (ts.pulse() != first.pulse()) {
                        remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, where, true, type);
+                       add_tempo_locked (tempo, pulse, true, type);
                } else {
                        first.set_type (type);
                        {
                                /* cannot move the first tempo section */
                                *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -881,15 +874,17 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
-               if (ts.beat() != first.beat()) {
+               if (ts.frame() != first.frame()) {
                        remove_tempo_locked (ts);
                        add_tempo_locked (tempo, frame, true, type);
                } else {
                        first.set_type (type);
+                       first.set_pulse (0.0);
+                       first.set_position_lock_style (AudioTime);
                        {
                                /* cannot move the first tempo section */
                                *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                }
        }
@@ -897,150 +892,61 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
        PropertyChanged (PropertyChange ());
 }
 
-void
-TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
-{
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-
-               if (ts.position_lock_style() == MusicTime) {
-                       std::cerr << "Music " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
-                       /* MusicTime */
-                       ts.set_beat (beat_where);
-                       Metrics::const_iterator i;
-
-                       TempoSection* prev_ts = 0;
-                       MetricSectionSorter cmp;
-                       metrics.sort (cmp);
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                                       if (t->beat() >= beat_where) {
-                                               break;
-                                       }
-
-                                       prev_ts = t;
-                               }
-                       }
-
-                       prev_ts->set_c_func_from_tempo_and_beat (ts.beats_per_minute(), ts.beat(), _frame_rate);
-                       ts.set_frame (prev_ts->frame_at_beat (ts.beat(), _frame_rate));
-               } else {
-                       std::cerr << "Audio " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
-
-                       /*AudioTime*/
-                       ts.set_frame (frame);
-                       MetricSectionFrameSorter fcmp;
-                       metrics.sort (fcmp);
-
-                       Metrics::const_iterator i;
-                       TempoSection* prev_ts = 0;
-                       TempoSection* next_ts = 0;
-
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                                       if (t->frame() >= frame) {
-                                               break;
-                                       }
-
-                                       prev_ts = t;
-                               }
-                       }
-
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                                       if (t->frame() > frame) {
-                                               next_ts = t;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (prev_ts) {
-                               /* set the start beat - we need to reset the function constant before beat calculations make sense*/
-                               prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame, _frame_rate));
-                               double beats = prev_ts->beat_at_frame (frame, _frame_rate);
-
-                               if (next_ts) {
-                                       if (next_ts->beat() < beats) {
-                                               /* with frame-based editing, it is possible to get in a
-                                                  situation where if the tempo was placed at the mouse pointer frame,
-                                                  the following music-based tempo would jump to an earlier frame,
-                                                  changing the beat beat of the moved tempo.
-                                                  in this case, we have to do some beat-based comparison TODO
-                                               */
-                                       } else if (prev_ts->beat() > beats) {
-                                               ts.set_beat (prev_ts->beat());
-                                       } else {
-                                               ts.set_beat (beats);
-                                       }
-                               } else {
-                                       ts.set_beat (beats);
-                                       ts.set_c_func (0.0);
-
-                               }
-                               MetricSectionSorter cmp;
-                               metrics.sort (cmp);
-                       }
-               }
-
-               recompute_map (false);
-       }
-
-       MetricPositionChanged (); // Emit Signal
-}
-
-void
-TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
+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, where, true, type);
+               ts = add_tempo_locked (tempo, pulse, true, type);
        }
 
-
        PropertyChanged (PropertyChange ());
+
+       return ts;
 }
 
-void
-TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
+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
-TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
+TempoSection*
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
 {
-       TempoSection* ts = new TempoSection (where, 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) {
-               recompute_map (false);
+               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) {
-               recompute_map (false);
+               solve_map (_metrics, t, t->frame());
        }
+
+       return t;
 }
 
 void
@@ -1048,15 +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.beat() != first.beat()) {
+
+               if (ms.movable()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
+                       add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
                } else {
+                       MeterSection& first (first_meter());
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
-                       recompute_map (true);
+                       first.set_position_lock_style (AudioTime);
                }
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -1067,58 +975,76 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const frame
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               MeterSection& first (first_meter());
-               if (ms.beat() != first.beat()) {
+
+               const double beat = ms.beat();
+               const BBT_Time bbt = ms.bbt();
+
+               if (ms.movable()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, frame, true);
+                       add_meter_locked (meter, frame, beat, bbt, true);
                } else {
+                       MeterSection& first (first_meter());
+                       TempoSection& first_t (first_tempo());
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
-                       recompute_map (true);
+                       first.set_position_lock_style (AudioTime);
+                       first.set_pulse (0.0);
+                       first.set_frame (frame);
+                       pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
+                       first.set_beat (beat);
+                       first_t.set_frame (first.frame());
+                       first_t.set_pulse (0.0);
+                       first_t.set_position_lock_style (AudioTime);
                }
+               recompute_map (_metrics);
        }
-
        PropertyChanged (PropertyChange ());
 }
 
 
-void
-TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
+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);
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (std::cerr);
+               dump (_metrics, std::cerr);
        }
 #endif
 
        PropertyChanged (PropertyChange ());
+
+       return m;
 }
 
-void
-TempoMap::add_meter (const Meter& meter, framepos_t frame)
+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, true);
+               m = add_meter_locked (meter, frame, beat, where, true);
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (std::cerr);
+               dump (_metrics, std::cerr);
        }
 #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
@@ -1132,39 +1058,34 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, boo
                where.beats = 1;
                where.bars++;
        }
-
        /* new meters *always* start on a beat. */
        where.ticks = 0;
-
-       do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
+       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) {
-               recompute_map (true);
+               solve_map (_metrics, new_meter, pulse);
        }
 
+       return new_meter;
 }
 
-void
-TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
+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());
 
-       /* MusicTime meters *always* start on 1|1|0. */
-       MeterSection* ms = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
-       BBT_Time bbt;
-       pair<double, BBT_Time> pr;
-       bbt.bars = 1;
-       bbt.beats = 1;
-       bbt.ticks = 0;
-       /* just a dummy - the actual beat should be applied in recompute_map() as thins is AudioTime */
-       pr.first = 0.0;
-       pr.second = bbt;
-       ms->set_beat (pr);
-       do_insert (ms);
+       double pulse = pulse_at_frame_locked (_metrics, frame);
+       new_meter->set_pulse (pulse);
+
+       do_insert (new_meter);
 
        if (recompute) {
-               recompute_map (true);
+               solve_map (_metrics, new_meter, frame);
        }
 
+       return new_meter;
 }
 
 void
@@ -1173,12 +1094,15 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
        Tempo newtempo (beats_per_minute, note_type);
        TempoSection* t;
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
                        {
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
-                               recompute_map (false);
+                               recompute_map (_metrics);
                        }
                        PropertyChanged (PropertyChange ());
                        break;
@@ -1198,7 +1122,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
        /* find the TempoSection immediately preceding "where"
         */
 
-       for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
+       for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
 
                if ((*i)->frame() > where) {
                        break;
@@ -1207,6 +1131,9 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
                        if (!first) {
                                first = t;
                        }
@@ -1229,7 +1156,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
                Glib::Threads::RWLock::WriterLock lm (lock);
                /* cannot move the first tempo section */
                *((Tempo*)prev) = newtempo;
-               recompute_map (false);
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -1240,13 +1167,13 @@ TempoMap::first_meter () const
 {
        const MeterSection *m = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
                        return *m;
                }
        }
 
-       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       fatal << _("programming error: no meter section in tempo map!") << endmsg;
        abort(); /*NOTREACHED*/
        return *m;
 }
@@ -1258,7 +1185,7 @@ TempoMap::first_meter ()
 
        /* CALLER MUST HOLD LOCK */
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
                        return *m;
                }
@@ -1276,9 +1203,14 @@ TempoMap::first_tempo () const
 
        /* CALLER MUST HOLD LOCK */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
-                       return *t;
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               return *t;
+                       }
                }
        }
 
@@ -1292,9 +1224,14 @@ TempoMap::first_tempo ()
 {
        TempoSection *t = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       return *t;
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               return *t;
+                       }
                }
        }
 
@@ -1302,98 +1239,133 @@ TempoMap::first_tempo ()
        abort(); /*NOTREACHED*/
        return *t;
 }
-
 void
-TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
+TempoMap::recompute_tempos (Metrics& metrics)
 {
-       /* CALLER MUST HOLD WRITE LOCK */
-
-       if (end < 0) {
-
-               /* we will actually stop once we hit
-                  the last metric.
-               */
-               end = max_framepos;
-
-       }
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
-
-       if (end == 0) {
-               /* silly call from Session::process() during startup
-                */
-               return;
-       }
+       TempoSection* prev_t = 0;
 
-       Metrics::const_iterator i;
-       TempoSection* prev_ts = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (prev_ts) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               t->set_pulse (0.0);
+                               prev_t = t;
+                               continue;
+                       }
+                       if (prev_t) {
                                if (t->position_lock_style() == AudioTime) {
-                                       if (prev_ts->type() == TempoSection::Ramp) {
-                                               prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
-                                               t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
-                                       } else {
-                                               prev_ts->set_c_func (0.0);
-                                               t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
-                                       }
-                               } else if (t->position_lock_style() == MusicTime) {
-                                       if (prev_ts->type() == TempoSection::Ramp) {
-                                               prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
-                                               t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
-                                       } else {
-                                               prev_ts->set_c_func (0.0);
-                                               t->set_frame (prev_ts->frame_at_beat (t->beat(), _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_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_t->set_c_func (0.0);
+}
 
+/* tempos must be positioned correctly */
+void
+TempoMap::recompute_meters (Metrics& metrics)
+{
        MeterSection* meter = 0;
+       MeterSection* prev_m = 0;
 
        for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
-               /* We now have the tempo map set. use it to set meter positions.*/
                if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
                        if (meter->position_lock_style() == AudioTime) {
-                               /* a frame based meter has to have a 1|1|0 bbt */
-                               pair<double, BBT_Time> pr;
-                               BBT_Time where;
-
-                               where.bars = 1;
-                               where.beats = 1;
-                               where.ticks = 0;
-
-                               pr.first = beat_at_frame (meter->frame());
-                               pr.second = where;
-                               meter->set_beat (pr);
+                               double pulse = 0.0;
+                               pair<double, BBT_Time> b_bbt;
+                               if (meter->movable()) {
+                                       const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
+                                       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);
                        } else {
-                               meter->set_frame (frame_at_tick (meter->tick()));
+                               double pulse = 0.0;
+                               pair<double, BBT_Time> new_beat;
+                               if (prev_m) {
+                                       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);
                        }
+
+                       prev_m = meter;
                }
        }
+       //dump (_metrics, std::cerr;
 }
 
-
-TempoMetric
-TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+void
+TempoMap::recompute_map (Metrics& metrics, framepos_t end)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       TempoMetric m (first_meter(), first_tempo());
+       /* CALLER MUST HOLD WRITE LOCK */
 
-       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
-          at something, because we insert the default tempo and meter during
-          TempoMap construction.
+       if (end < 0) {
 
-          now see if we can find better candidates.
-       */
+               /* we will actually stop once we hit
+                  the last metric.
+               */
+               end = max_framepos;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       }
+
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
+
+       if (end == 0) {
+               /* silly call from Session::process() during startup
+                */
+               return;
+       }
+
+       recompute_tempos (metrics);
+       recompute_meters (metrics);
+}
+
+TempoMetric
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       TempoMetric m (first_meter(), first_tempo());
+
+       /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
+          at something, because we insert the default tempo and meter during
+          TempoMap construction.
+
+          now see if we can find better candidates.
+       */
+
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
                if ((*i)->frame() > frame) {
                        break;
@@ -1408,6 +1380,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 
        return m;
 }
+
 /* XX meters only */
 TempoMetric
 TempoMap::metric_at (BBT_Time bbt) const
@@ -1422,7 +1395,7 @@ TempoMap::metric_at (BBT_Time bbt) const
           now see if we can find better candidates.
        */
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                MeterSection* mw;
                if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
                        BBT_Time section_start (mw->bbt());
@@ -1438,104 +1411,287 @@ 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;
+               }
+
+       }
+       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);
+       return pulse_at_beat_locked (_metrics, beat);
+}
 
-       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
+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;
+               }
        }
-       bbt = beats_to_bbt_locked (beat_at_frame (frame));
+
+       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 (Timecode::BBT_Time bbt)
+TempoMap::beat_at_pulse (const double& pulse) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_to_beats_locked (bbt);
+       return beat_at_pulse_locked (_metrics, pulse);
 }
 
 double
-TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
+TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
 {
-       /* CALLER HOLDS READ LOCK */
+       /* HOLD (at least) THE READER LOCK */
+       TempoSection* prev_t = 0;
 
-       double accumulated_beats = 0.0;
-       double accumulated_bars = 0.0;
-       MeterSection* prev_ms = 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;
+               }
+       }
 
-       Metrics::const_iterator i;
+       /* treated as constant for this ts */
+       const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
 
-       for (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();
+       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 ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
-                               break;
+                       if (prev_t && t->pulse() > pulse) {
+                               return prev_t->frame_at_pulse (pulse, _frame_rate);
                        }
-                       if (prev_ms) {
-                               accumulated_beats += m->beat() - prev_ms->beat();
-                               accumulated_bars += bars_to_m;
+
+                       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
+TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
+{
+       /* CALLER HOLDS READ LOCK */
+
+       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) {
+                       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;
+                               }
                        }
-                       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 (double beats)
+double
+TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beats_to_bbt_locked (beats);
+       return bbt_to_beats_locked (_metrics, bbt);
 }
 
 Timecode::BBT_Time
-TempoMap::beats_to_bbt_locked (double beats)
+TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
 {
        /* CALLER HOLDS READ LOCK */
+       MeterSection* prev_m = 0;
+       const double beats = (b < 0.0) ? 0.0 : b;
 
-       MeterSection* prev_ms = 0;
-       uint32_t accumulated_bars = 0;
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-       Metrics::const_iterator i;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_m) {
+                               if (m->beat() > beats) {
+                                       /* this is the meter after the one our beat is on*/
+                                       break;
+                               }
+                       }
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                       prev_m = m;
+               }
+       }
+
+       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;
+
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
+
+       /* 0 0 0 to 1 1 0 - based mapping*/
+       ++ret.bars;
+       ++ret.beats;
+
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
+       }
+
+       if (ret.beats >= prev_m->divisions_per_bar() + 1) {
+               ++ret.bars;
+               ret.beats = 1;
+       }
+
+       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_m = 0;
+
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                MeterSection* m = 0;
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                       if (beats < m->beat()) {
-                               /* this is the meter after the one our beat is on*/
-                               break;
-                       }
-
-                       if (prev_ms) {
-                               /* we need a whole number of bars. */
-                               accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
+                       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;
+                               }
                        }
 
-                       prev_ms = m;
+                       prev_m = m;
                }
        }
 
-       double const beats_in_ms = beats - prev_ms->beat();
-       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;
 
@@ -1543,7 +1699,7 @@ TempoMap::beats_to_bbt_locked (double beats)
        ret.beats = (uint32_t) floor (remaining_beats);
        ret.bars = total_bars;
 
-       /* 0 0 0 to 1 1 0 - based mapping*/
+       /* 0 0 0 to 1 1 0 mapping*/
        ++ret.bars;
        ++ret.beats;
 
@@ -1552,7 +1708,7 @@ TempoMap::beats_to_bbt_locked (double beats)
                ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       if (ret.beats > prev_ms->divisions_per_bar()) {
+       if (ret.beats >= prev_m->divisions_per_bar() + 1) {
                ++ret.bars;
                ret.beats = 1;
        }
@@ -1560,139 +1716,711 @@ TempoMap::beats_to_bbt_locked (double beats)
        return ret;
 }
 
-double
-TempoMap::tick_at_frame (framecnt_t frame) const
+void
+TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
 {
-       /* HOLD (at least) THE READER LOCK */
 
-       Metrics::const_iterator i;
-       TempoSection* prev_ts = 0;
-       double accumulated_ticks = 0.0;
+       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);
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       bbt = beats_to_bbt_locked (_metrics, beat);
+}
+
+framepos_t
+TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
+{
+       /* HOLD THE READER LOCK */
+
+       const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
+       return ret;
+}
+
+framepos_t
+TempoMap::frame_time (const BBT_Time& bbt)
+{
+       if (bbt.bars < 1) {
+               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
+               return 0;
+       }
+
+       if (bbt.beats < 1) {
+               throw std::logic_error ("beats are counted from one");
+       }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return frame_time_locked (_metrics, bbt);
+}
+
+bool
+TempoMap::check_solved (Metrics& metrics, bool by_frame)
+{
+       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) {
+                               if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
+                                       return false;
+                               }
+
+                               if (t->frame() == prev_t->frame()) {
+                                       return false;
+                               }
 
+                               /* precision check ensures pulses and frames align.*/
+                               if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
+                                       return false;
+                               }
+                       }
+                       prev_t = t;
+               }
+       }
+
+       return true;
+}
+
+bool
+TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
+{
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->movable()) {
+                               t->set_active (true);
+                               continue;
+                       }
+                       if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
+                               t->set_active (false);
+                               t->set_pulse (0.0);
+                       } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
+                               t->set_active (true);
+                       } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
 
-                       if ((prev_ts) && frame < t->frame()) {
-                               /*the previous ts is the one containing the frame */
-                               return prev_ts->tick_at_frame (frame, _frame_rate);
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
+{
+       TempoSection* prev_t = 0;
+       TempoSection* section_prev = 0;
+       framepos_t first_m_frame = 0;
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (!m->movable()) {
+                               first_m_frame = m->frame();
+                               break;
+                       }
+               }
+       }
+       if (section->movable() && frame <= first_m_frame) {
+               return false;
+       }
+
+       section->set_active (true);
+       section->set_frame (frame);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (prev_t) {
+                               if (t == section) {
+                                       section_prev = prev_t;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       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_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_t = t;
+               }
+       }
+
+       if (section_prev) {
+               section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
+               section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
+       }
+
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+
+       MetricSectionFrameSorter fcmp;
+       imaginary.sort (fcmp);
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+#if (0)
+       MetricSectionSorter cmp;
+       imaginary.sort (cmp);
+       if (section->position_lock_style() == MusicTime) {
+               /* we're setting the frame */
+               section->set_position_lock_style (AudioTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (MusicTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, true)) {
+               recompute_meters (imaginary);
+               return true;
+       }
+#endif
+       //dump (imaginary, std::cerr);
+
+       return false;
+}
+
+bool
+TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
+{
+       TempoSection* prev_t = 0;
+       TempoSection* section_prev = 0;
+
+       section->set_pulse (pulse);
+
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (!t->movable()) {
+                               t->set_pulse (0.0);
+                               prev_t = t;
+                               continue;
+                       }
+                       if (prev_t) {
+                               if (t == section) {
+                                       section_prev = prev_t;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       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_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_t = t;
+               }
+       }
+       if (section_prev) {
+               section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
+               section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
+       }
+
+       if (section->position_lock_style() == AudioTime) {
+               /* we're setting the pulse */
+               section->set_position_lock_style (MusicTime);
+               recompute_tempos (imaginary);
+               section->set_position_lock_style (AudioTime);
+       } else {
+               recompute_tempos (imaginary);
+       }
+
+       if (check_solved (imaginary, false)) {
+               recompute_meters (imaginary);
+               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);
+}
 
-                       if (prev_ts && t->frame() > prev_ts->frame()) {
-                               accumulated_ticks = t->tick();
+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));
+       }
 
-                       prev_ts = t;
+       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);
        }
 
-       /* treated as constant for this ts */
-       double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+       double const beat = bbt_to_beats_locked (copy, bbt);
+       bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
 
-       return ticks_in_section + accumulated_ticks;
+       Metrics::const_iterator d = copy.begin();
+       while (d != copy.end()) {
+               delete (*d);
+               ++d;
+       }
 
+       return ret;
 }
 
-framecnt_t
-TempoMap::frame_at_tick (double tick) const
+/**
+* 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)
 {
-       /* HOLD THE READER LOCK */
-
-       double accumulated_ticks = 0.0;
-       const TempoSection* prev_ts = 0;
-
-       Metrics::const_iterator i;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics future_map;
+       framepos_t ret = 0;
+       TempoSection* new_section = copy_metrics_and_point (future_map, section);
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (prev_ts && tick < t->tick()) {
-                               /* prev_ts is the one affecting us. */
-                               return prev_ts->frame_at_tick (tick, _frame_rate);
-                       }
+       const double beat = bbt_to_beats_locked (future_map, bbt);
 
-                       accumulated_ticks = t->tick();
-                       prev_ts = t;
-               }
+       if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
+               ret = new_section->frame();
+       } else {
+               ret = frame_at_beat_locked (_metrics, beat);
        }
-       /* must be treated as constant, irrespective of _type */
-       double const ticks_in_section = tick - accumulated_ticks;
-       double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
-
-       framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
 
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
        return ret;
 }
 
 double
-TempoMap::beat_at_frame (framecnt_t frame) const
+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);
 
-       return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
+       if (solve_map (future_map, new_section, frame)) {
+               ret = new_section->pulse();
+       } else {
+               ret = pulse_at_frame_locked (_metrics, frame);
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+       return ret;
 }
 
-framecnt_t
-TempoMap::frame_at_beat (double beat) const
+void
+TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       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);
+               }
+       }
 
-       return frame_at_tick (beat * BBT_Time::ticks_per_beat);
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       MetricPositionChanged (); // Emit Signal
 }
 
-framepos_t
-TempoMap::frame_time (const BBT_Time& bbt)
+void
+TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
 {
-       if (bbt.bars < 1) {
-               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
-               return 0;
+       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));
+               }
        }
 
-       if (bbt.beats < 1) {
-               throw std::logic_error ("beats are counted from one");
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
        }
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
 
-       return ret;
+       MetricPositionChanged (); // Emit Signal
 }
 
-
-framecnt_t
-TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
+void
+TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
 {
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, frame);
+       }
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       Metrics::const_iterator i;
-       TempoSection* first = 0;
-       TempoSection* second = 0;
+       MetricPositionChanged (); // Emit Signal
+}
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
+void
+TempoMap::gui_move_meter (MeterSection* ms, const double&  beat)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
+       }
 
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+       MetricPositionChanged (); // Emit Signal
+}
 
-                       if ((*i)->frame() > pos) {
-                               second = t;
-                               break;
-                       }
+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);
 
-                       first = t;
+               if (check_solved (future_map, true)) {
+                       ts->set_beats_per_minute (bpm.beats_per_minute());
+                       recompute_map (_metrics);
+                       can_solve = true;
                }
        }
-       if (first && second) {
-               double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
-               double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-               double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
 
-               return time_at_bbt - pos;
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
        }
+       if (can_solve) {
+               MetricPositionChanged (); // Emit Signal
+       }
+       return can_solve;
+}
 
-       double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-       return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
+framecnt_t
+TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       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 frame_at_beat_locked (_metrics, total_beats);
 }
 
 framepos_t
@@ -1711,10 +2439,9 @@ framepos_t
 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
+       uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
        uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
-       uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
+       uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
 
        ticks -= beats * BBT_Time::ticks_per_beat;
 
@@ -1798,7 +2525,61 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        /* on the subdivision, do nothing */
                }
        }
-       return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
+
+       const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+
+       return ret_frame;
+}
+
+void
+TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
+{
+       if (sub_num == -1) {
+               const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+               if ((double) when.beats > bpb / 2.0) {
+                       ++when.bars;
+               }
+               when.beats = 1;
+               when.ticks = 0;
+               return;
+       } else if (sub_num == 0) {
+               const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+               if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
+                       ++when.beats;
+                       while ((double) when.beats > bpb) {
+                               ++when.bars;
+                               when.beats -= (uint32_t) floor (bpb);
+                       }
+               }
+               when.ticks = 0;
+               return;
+       }
+       const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
+       double rem;
+       if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
+               /* closer to the next subdivision, so shift forward */
+
+               when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
+
+               if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
+                       ++when.beats;
+                       when.ticks -= Timecode::BBT_Time::ticks_per_beat;
+               }
+
+       } else if (rem > 0) {
+               /* closer to previous subdivision, so shift backward */
+
+               if (rem > when.ticks) {
+                       if (when.beats == 0) {
+                               /* can't go backwards past zero, so ... */
+                       }
+                       /* step back to previous beat */
+                       --when.beats;
+                       when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
+               } else {
+                       when.ticks = when.ticks - rem;
+               }
+       }
 }
 
 framepos_t
@@ -1806,9 +2587,8 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       double const beat_at_framepos = beat_at_frame (frame);
-
-       BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
+       const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
+       BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
 
        switch (type) {
        case Bar:
@@ -1816,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;
@@ -1844,11 +2624,11 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 
        case Beat:
                if (dir < 0) {
-                       return frame_at_beat (floor (beat_at_framepos));
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
                } else if (dir > 0) {
-                       return frame_at_beat (ceil (beat_at_framepos));
+                       return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
                } else {
-                       return frame_at_beat (floor (beat_at_framepos + 0.5));
+                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
                }
                break;
        }
@@ -1861,16 +2641,19 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    framepos_t lower, framepos_t upper)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
-       uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
-
-       while (cnt <= upper_beat) {
-               framecnt_t const pos = frame_at_beat (cnt);
-               MeterSection const meter = meter_section_at (pos);
-               Tempo const tempo = tempo_at (pos);
-               BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
-
-               points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
+       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 && 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;
        }
 }
@@ -1879,7 +2662,12 @@ const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return tempo_section_at_locked (_metrics, frame);
+}
 
+const TempoSection&
+TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
+{
        Metrics::const_iterator i;
        TempoSection* prev = 0;
 
@@ -1887,8 +2675,10 @@ TempoMap::tempo_section_at (framepos_t frame) const
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                       if ((*i)->frame() > frame) {
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (prev && t->frame() > frame) {
                                break;
                        }
 
@@ -1904,23 +2694,26 @@ TempoMap::tempo_section_at (framepos_t frame) const
        return *prev;
 }
 
+
 /* don't use this to calculate length (the tempo is only correct for this frame).
    do that stuff based on the beat_at_frame and frame_at_beat api
 */
 double
-TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
+TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const TempoSection* ts_at = &tempo_section_at (frame);
+       const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
        const TempoSection* ts_after = 0;
        Metrics::const_iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
+                       if (!t->active()) {
+                               continue;
+                       }
                        if ((*i)->frame() > frame) {
                                ts_after = t;
                                break;
@@ -1936,49 +2729,64 @@ TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
 }
 
 const Tempo
-TempoMap::tempo_at (framepos_t frame) const
+TempoMap::tempo_at (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return tempo_at_locked (_metrics, frame);
+}
 
-       TempoMetric m (metric_at (frame));
-       TempoSection* prev_ts = 0;
+const Tempo
+TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
+{
+       TempoSection* prev_t = 0;
 
        Metrics::const_iterator i;
 
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
+       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                TempoSection* t;
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if ((prev_ts) && t->frame() > frame) {
-                               /* this is the one past frame */
-                               double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
-                               Tempo const ret_tempo (ret, m.tempo().note_type ());
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if ((prev_t) && t->frame() > frame) {
+                               /* t is the section past frame */
+                               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;
                }
        }
 
-       return m.tempo();
+       const double ret = prev_t->beats_per_minute();
+       const Tempo ret_tempo (ret, prev_t->note_type ());
 
+       return ret_tempo;
 }
 
 const MeterSection&
 TempoMap::meter_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_locked (_metrics, frame);
+}
+
+const MeterSection&
+TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
+{
        Metrics::const_iterator i;
        MeterSection* prev = 0;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* t;
+               MeterSection* m;
 
-               if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                       if ((*i)->frame() > frame) {
+                       if (prev && (*i)->frame() > frame) {
                                break;
                        }
 
-                       prev = t;
+                       prev = m;
                }
        }
 
@@ -1997,6 +2805,87 @@ TempoMap::meter_at (framepos_t frame) const
        return m.meter();
 }
 
+const MeterSection&
+TempoMap::meter_section_at (const double& beat) const
+{
+       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_m && m->beat() > beat) {
+                               break;
+                       }
+                       prev_m = m;
+               }
+
+       }
+       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&
 TempoMap::get_state ()
 {
@@ -2005,7 +2894,7 @@ TempoMap::get_state ()
 
        {
                Glib::Threads::RWLock::ReaderLock lm (lock);
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
+               for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                        root->add_child_nocopy ((*i)->get_state());
                }
        }
@@ -2021,9 +2910,8 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                XMLNodeList nlist;
                XMLNodeConstIterator niter;
-               Metrics old_metrics (metrics);
-               MeterSection* last_meter = 0;
-               metrics.clear();
+               Metrics old_metrics (_metrics);
+               _metrics.clear();
 
                nlist = node.children();
 
@@ -2034,18 +2922,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        TempoSection* ts = new TempoSection (*child);
-                                       metrics.push_back (ts);
-
-                                       if (ts->bar_offset() < 0.0) {
-                                               if (last_meter) {
-                                                       //ts->update_bar_offset_from_bbt (*last_meter);
-                                               }
-                                       }
+                                       _metrics.push_back (ts);
                                }
 
                                catch (failed_constructor& err){
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
 
@@ -2053,13 +2935,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                try {
                                        MeterSection* ms = new MeterSection (*child);
-                                       metrics.push_back (ms);
-                                       last_meter = ms;
+                                       _metrics.push_back (ms);
                                }
 
                                catch (failed_constructor& err) {
                                        error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
-                                       metrics = old_metrics;
+                                       _metrics = old_metrics;
                                        break;
                                }
                        }
@@ -2067,47 +2948,42 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                if (niter == nlist.end()) {
                        MetricSectionSorter cmp;
-                       metrics.sort (cmp);
+                       _metrics.sort (cmp);
                }
-               /* check for legacy sessions where bbt was the base musical unit for tempo */
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-                       MeterSection* prev_ms;
-                       TempoSection* prev_ts;
-                       if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               if (prev_ms->beat() < 0.0) {
-                                       /*XX we cannot possibly make this work??. */
-                                       pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
-                                       prev_ms->set_beat (start);
-                               }
-                       } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               if (prev_ts->beat() < 0.0) {
-                                       double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
-                                       prev_ts->set_beat (start);
 
+               /* check for legacy sessions where bbt was the base musical unit for tempo */
+               for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+                       TempoSection* t;
+                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                               if (t->legacy_bbt().bars != 0) {
+                                       fix_legacy_session();
+                                       break;
                                }
+                               break;
                        }
                }
+
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
 
-               Metrics::iterator prev = metrics.end();
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-                       if (prev != metrics.end()) {
+               Metrics::iterator prev = _metrics.end();
+               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->beat() == ms->beat()) {
-                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
-                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << 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->beat() == ts->beat()) {
-                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
-                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << 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;
                                        }
                                }
@@ -2115,7 +2991,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        prev = i;
                }
 
-               recompute_map (true, -1);
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
@@ -2124,22 +3000,30 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 }
 
 void
-TempoMap::dump (std::ostream& o) const
+TempoMap::dump (const Metrics& metrics, std::ostream& o) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
        const MeterSection* m;
        const TempoSection* t;
+       const TempoSection* prev_t = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
-                         << t->movable() << ')' << endl;
+                       o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
+                         << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
+                       o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
+                       if (prev_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_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()
-                         << " (movable? " << m->movable() << ')' << endl;
+                         << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
                }
        }
+       o << "------" << std::endl;
 }
 
 int
@@ -2148,7 +3032,7 @@ TempoMap::n_tempos() const
        Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<const TempoSection*>(*i) != 0) {
                        cnt++;
                }
@@ -2163,7 +3047,7 @@ TempoMap::n_meters() const
        Glib::Threads::RWLock::ReaderLock lm (lock);
        int cnt = 0;
 
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<const MeterSection*>(*i) != 0) {
                        cnt++;
                }
@@ -2177,7 +3061,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        if ((*i)->frame() >= where && (*i)->movable ()) {
                                (*i)->set_frame ((*i)->frame() + amount);
                        }
@@ -2205,7 +3089,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                bool first = true;
                MetricSection* prev = 0;
 
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
+               for (i = _metrics.begin(); i != _metrics.end(); ++i) {
 
                        BBT_Time bbt;
                        //TempoMetric metric (*meter, *tempo);
@@ -2214,10 +3098,13 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        if (prev) {
                                if (ts){
                                        if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               ts->set_beat (t->beat());
+                                               if (!t->active()) {
+                                                       continue;
+                                               }
+                                               ts->set_pulse (t->pulse());
                                        }
                                        if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               ts->set_beat (m->beat());
+                                               ts->set_pulse (m->pulse());
                                        }
                                        ts->set_frame (prev->frame());
 
@@ -2226,10 +3113,16 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                        if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
                                                pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
                                                ms->set_beat (start);
+                                               ms->set_pulse (m->pulse());
                                        }
                                        if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
+                                               if (!t->active()) {
+                                                       continue;
+                                               }
+                                               const double beat = beat_at_pulse_locked (_metrics, t->pulse());
+                                               pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
                                                ms->set_beat (start);
+                                               ms->set_pulse (t->pulse());
                                        }
                                        ms->set_frame (prev->frame());
                                }
@@ -2242,9 +3135,12 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        // cerr << bbt << endl;
 
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               t->set_beat (beat_at_frame (m->frame()));
+                               if (!t->active()) {
+                                       continue;
+                               }
+                               t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
                                tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
                        } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
                                bbt_time (m->frame(), bbt);
 
@@ -2267,10 +3163,11 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                                bbt.beats = 1;
                                        }
                                }
-                               pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
+                               pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
                                m->set_beat (start);
+                               m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
                                meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                abort(); /*NOTREACHED*/
@@ -2279,7 +3176,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        prev = (*i);
                }
 
-               recompute_map (true);
+               recompute_map (_metrics);
        }
 
 
@@ -2298,7 +3195,7 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
        bool meter_after = false; // is there a meter marker likewise?
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
                                metric_kill_list.push_back(*i);
                                TempoSection *lt = dynamic_cast<TempoSection*> (*i);
@@ -2334,12 +3231,12 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
 
                //remove all the remaining metrics
                for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
-                       metrics.remove(*i);
+                       _metrics.remove(*i);
                        moved = true;
                }
 
                if (moved) {
-                       recompute_map (true);
+                       recompute_map (_metrics);
                }
        }
        PropertyChanged (PropertyChange ());
@@ -2366,169 +3263,25 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
-       cerr << "framepos_plus_bbt - untested" << endl;
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       Metrics::const_iterator i;
-       const MeterSection* meter;
-       const MeterSection* m;
-       const TempoSection* tempo;
-       const TempoSection* next_tempo = 0;
-       const TempoSection* t;
-       double frames_per_beat;
-       framepos_t effective_pos = max (pos, (framepos_t) 0);
-
-       meter = &first_meter ();
-       tempo = &first_tempo ();
-
-       assert (meter);
-       assert (tempo);
-
-       /* find the starting metrics for tempo & meter */
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-
-               if ((*i)->frame() > effective_pos) {
-                       break;
-               }
-
-               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       tempo = t;
-               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       meter = m;
-               }
-       }
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               if ((*i)->frame() > effective_pos) {
-                       if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                               next_tempo = t;
-                       }
-                       break;
-               }
-       }
-
-       /* We now have:
-
-          meter -> the Meter for "pos"
-          tempo -> the Tempo for "pos"
-          next_tempo -> the Tempo after "pos" or 0
-          i     -> for first new metric after "pos", possibly metrics.end()
-       */
-
-       /* now comes the complicated part. we have to add one beat a time,
-          checking for a new metric on every beat.
-       */
-
-       uint64_t bars = 0;
-       /* fpb is used for constant tempo */
-       frames_per_beat = tempo->frames_per_beat (_frame_rate);
-
-       while (op.bars) {
-
-               bars++;
-               op.bars--;
-
-               /* check if we need to use a new metric section: has adding frames moved us
-                  to or after the start of the next metric section? in which case, use it.
-               */
-
-               if (i != metrics.end()) {
-                       if ((*i)->frame() <= pos) {
-
-                               /* about to change tempo or meter, so add the
-                                * number of frames for the bars we've just
-                                * traversed before we change the
-                                * frames_per_beat value.
-                                */
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       next_tempo = t;
-                               }
-
-                               if (next_tempo) {
-                                       pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
-                               } else {
-                                       pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
-                               }
-
-                               bars = 0;
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       tempo = t;
-                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                                       meter = m;
-                               }
-                               ++i;
-                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
-                       }
-               }
-
-       }
-
-       if (next_tempo) {
-               pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
-       } else {
-               pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
-       }
-
-       uint64_t beats = 0;
-
-       while (op.beats) {
-
-               /* given the current meter, have we gone past the end of the bar ? */
-
-               beats++;
-               op.beats--;
-
-               /* check if we need to use a new metric section: has adding frames moved us
-                  to or after the start of the next metric section? in which case, use it.
-               */
-
-               if (i != metrics.end()) {
-                       if ((*i)->frame() <= pos) {
-
-                               /* about to change tempo or meter, so add the
-                                * number of frames for the beats we've just
-                                * traversed before we change the
-                                * frames_per_beat value.
-                                */
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       next_tempo = t;
-                               }
-
-                               if (next_tempo) {
-                                       pos += tempo->frame_at_beat (beats, _frame_rate);
-                               } else {
-                                       pos += llrint (beats * frames_per_beat);
-                               }
-
-                               beats = 0;
-
-                               if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                                       tempo = t;
-                               } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                                       meter = m;
-                               }
-                               ++i;
-                               frames_per_beat = tempo->frames_per_beat (_frame_rate);
-                       }
-               }
-       }
-
-       if (next_tempo) {
-               pos += tempo->frame_at_beat (beats, _frame_rate);
-       } else {
-               pos += llrint (beats * frames_per_beat);
+       BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
+       pos_bbt.ticks += op.ticks;
+       if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
+               ++pos_bbt.beats;
+               pos_bbt.ticks -= BBT_Time::ticks_per_beat;
        }
-
-       if (op.ticks) {
-               pos += tempo->frame_at_tick (op.ticks, _frame_rate);
+       pos_bbt.beats += op.beats;
+       /* the meter in effect will start on the bar */
+       double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
+       while (pos_bbt.beats >= divisions_per_bar + 1) {
+               ++pos_bbt.bars;
+               divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
+               pos_bbt.beats -= divisions_per_bar;
        }
+       pos_bbt.bars += op.bars;
 
-       return pos;
-
+       return frame_time_locked (_metrics, pos_bbt);
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
@@ -2537,7 +3290,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
-       return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
+       return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
 }
 
 struct bbtcmp {
@@ -2567,7 +3320,7 @@ operator<< (std::ostream& o, const MetricSection& section) {
        if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
                o << *((const Tempo*) ts);
        } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
-               //o << *((const Meter*) ms);
+               o << *((const Meter*) ms);
        }
 
        return o;