Fix copy/paste typo -- MacOSX builds
[ardour.git] / libs / ardour / tempo.cc
index 9c367ea4bd06d89ca65315612eff20374ee66a2b..bb61fb70127a368dfd57d13a68226ffc9adddd6d 100644 (file)
@@ -45,7 +45,19 @@ using Timecode::BBT_Time;
 /* _default tempo is 4/4 qtr=120 */
 
 Meter    TempoMap::_default_meter (4.0, 4.0);
 /* _default tempo is 4/4 qtr=120 */
 
 Meter    TempoMap::_default_meter (4.0, 4.0);
-Tempo    TempoMap::_default_tempo (120.0);
+Tempo    TempoMap::_default_tempo (120.0, 4.0, 120.0);
+
+framepos_t
+MetricSection::frame_at_minute (const double& time) const
+{
+       return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
+}
+
+double
+MetricSection::minute_at_frame (const framepos_t& frame) const
+{
+       return (frame / (double) _sample_rate) / 60.0;
+}
 
 /***********************************************************************/
 
 
 /***********************************************************************/
 
@@ -59,7 +71,7 @@ Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
           The return value IS NOT interpretable in terms of "beats".
        */
 
           The return value IS NOT interpretable in terms of "beats".
        */
 
-       return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
+       return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
 }
 
 double
 }
 
 double
@@ -72,12 +84,14 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
-TempoSection::TempoSection (const XMLNode& node)
-       : MetricSection (0.0, 0, MusicTime, true)
+TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
+       : MetricSection (0.0, 0, MusicTime, true, sample_rate)
        , Tempo (TempoMap::default_tempo())
        , Tempo (TempoMap::default_tempo())
-       , _c_func (0.0)
+       , _c (0.0)
        , _active (true)
        , _locked_to_meter (false)
        , _active (true)
        , _locked_to_meter (false)
+       , _clamped (false)
+       , _legacy_end (false)
 {
        XMLProperty const * prop;
        LocaleGuard lg;
 {
        XMLProperty const * prop;
        LocaleGuard lg;
@@ -110,19 +124,18 @@ TempoSection::TempoSection (const XMLNode& node)
        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;
        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;
+                       throw failed_constructor();
                } else {
                } else {
-                       set_frame (frame);
+                       set_minute (minute_at_frame (frame));
                }
        }
 
                }
        }
 
-       if ((prop = node.property ("beats-per-minute")) == 0) {
-               error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
-               throw failed_constructor();
-       }
-
-       if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
-               error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
-               throw failed_constructor();
+       /* XX replace old beats-per-minute name with note-types-per-minute */
+       if ((prop = node.property ("beats-per-minute")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
+                       error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
+                       throw failed_constructor();
+               }
        }
 
        if ((prop = node.property ("note-type")) == 0) {
        }
 
        if ((prop = node.property ("note-type")) == 0) {
@@ -135,12 +148,40 @@ TempoSection::TempoSection (const XMLNode& node)
                }
        }
 
                }
        }
 
+       if ((prop = node.property ("clamped")) == 0) {
+               warning << _("TempoSection XML node has no \"clamped\" property") << endmsg;
+               set_clamped (false);
+       } else {
+               set_clamped (string_is_affirmative (prop->value()));
+       }
+
+       /* XX replace old end-beats-per-minute name with note-types-per-minute */
+       if ((prop = node.property ("end-beats-per-minute")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
+                       info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
+                       //throw failed_constructor();
+                       _end_note_types_per_minute = _note_types_per_minute;
+                       _legacy_end = true;
+               }
+       } else {
+               _legacy_end = true;
+       }
+
+       if ((prop = node.property ("tempo-type")) != 0) {
+               TempoSection::Type old_type;
+
+               old_type = Type (string_2_enum (prop->value(), old_type));
+               if (old_type == TempoSection::Constant) {
+                       _end_note_types_per_minute = _note_types_per_minute;
+               }
+       }
+
        if ((prop = node.property ("movable")) == 0) {
                error << _("TempoSection XML node has no \"movable\" property") << endmsg;
                throw failed_constructor();
        }
 
        if ((prop = node.property ("movable")) == 0) {
                error << _("TempoSection XML node has no \"movable\" property") << endmsg;
                throw failed_constructor();
        }
 
-       set_movable (string_is_affirmative (prop->value()));
+       set_initial (!string_is_affirmative (prop->value()));
 
        if ((prop = node.property ("active")) == 0) {
                warning << _("TempoSection XML node has no \"active\" property") << endmsg;
 
        if ((prop = node.property ("active")) == 0) {
                warning << _("TempoSection XML node has no \"active\" property") << endmsg;
@@ -149,14 +190,8 @@ TempoSection::TempoSection (const XMLNode& node)
                set_active (string_is_affirmative (prop->value()));
        }
 
                set_active (string_is_affirmative (prop->value()));
        }
 
-       if ((prop = node.property ("tempo-type")) == 0) {
-               _type = Constant;
-       } else {
-               _type = Type (string_2_enum (prop->value(), _type));
-       }
-
        if ((prop = node.property ("lock-style")) == 0) {
        if ((prop = node.property ("lock-style")) == 0) {
-               if (movable()) {
+               if (!initial()) {
                        set_position_lock_style (MusicTime);
                } else {
                        set_position_lock_style (AudioTime);
                        set_position_lock_style (MusicTime);
                } else {
                        set_position_lock_style (AudioTime);
@@ -166,10 +201,19 @@ TempoSection::TempoSection (const XMLNode& node)
        }
 
        if ((prop = node.property ("locked-to-meter")) == 0) {
        }
 
        if ((prop = node.property ("locked-to-meter")) == 0) {
-               set_locked_to_meter (false);
+               if (initial()) {
+                       set_locked_to_meter (true);
+               } else {
+                       set_locked_to_meter (false);
+               }
        } else {
                set_locked_to_meter (string_is_affirmative (prop->value()));
        }
        } else {
                set_locked_to_meter (string_is_affirmative (prop->value()));
        }
+
+       /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
+       if (initial()) {
+               set_locked_to_meter (true);
+       }
 }
 
 XMLNode&
 }
 
 XMLNode&
@@ -183,108 +227,150 @@ TempoSection::get_state() const
        root->add_property ("pulse", buf);
        snprintf (buf, sizeof (buf), "%li", frame());
        root->add_property ("frame", buf);
        root->add_property ("pulse", buf);
        snprintf (buf, sizeof (buf), "%li", frame());
        root->add_property ("frame", buf);
-       snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
+       snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%lf", _note_type);
        root->add_property ("note-type", buf);
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%lf", _note_type);
        root->add_property ("note-type", buf);
-       snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
+       snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no");
+       root->add_property ("clamped", buf);
+       snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
+       root->add_property ("end-beats-per-minute", buf);
+       snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
        root->add_property ("movable", buf);
        snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
        root->add_property ("active", buf);
        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()));
        root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
 
        return *root;
 }
 
        root->add_property ("lock-style", enum_2_string (position_lock_style()));
        root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
 
        return *root;
 }
 
-void
-TempoSection::set_type (Type type)
+/** returns the Tempo at the session-relative minute.
+*/
+Tempo
+TempoSection::tempo_at_minute (const double& m) const
 {
 {
-       _type = type;
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
+       if (constant) {
+               return Tempo (note_types_per_minute(), note_type());
+       }
+
+       return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
 }
 
 }
 
-/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
+/** returns the session relative minute where the supplied tempo in note types per minute occurs.
+ *  @param ntpm the tempo in mote types per minute used to calculate the returned minute
+ *  @param p the pulse used to calculate the returned minute for constant tempi
+ *  @return the minute at the supplied tempo
+ *
+ *  note that the note_type is currently ignored in this function. see below.
+ *
+*/
+
+/** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
+ *  there should be no ramp between the two even if we are ramped.
+ *  in other words a ramp should only place a curve on note_types_per_minute.
+ *  we should be able to use Tempo note type here, but the above
+ *  complicates things a bit.
 */
 double
 */
 double
-TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
 {
 {
-
-       if (_type == Constant || _c_func == 0.0) {
-               return pulses_per_minute();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return ((p - pulse()) / pulses_per_minute()) + minute();
        }
 
        }
 
-       return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
+       return _time_at_tempo (ntpm) + minute();
 }
 
 }
 
-/** returns the zero-based frame (relative to session)
-   where the tempo in whole pulses per minute occurs in this section.
-   pulse p is only used for constant tempos.
-   note that the tempo map may have multiple such values.
-*/
-framepos_t
-TempoSection::frame_at_tempo (const double& ppm, const double& p, const framecnt_t& frame_rate) const
+/** returns the Tempo at the supplied whole-note pulse.
+ */
+Tempo
+TempoSection::tempo_at_pulse (const double& p) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return ((p - pulse()) * frames_per_pulse (frame_rate))  + frame();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+
+       if (constant) {
+               return Tempo (note_types_per_minute(), note_type());
        }
 
        }
 
-       return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
+       return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
 }
 }
-/** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
+
+/** returns the whole-note pulse where a tempo in note types per minute occurs.
+ *  constant tempi require minute m.
+ *  @param ntpm the note types per minute value used to calculate the returned pulse
+ *  @param m the minute used to calculate the returned pulse if the tempo is constant
+ *  @return the whole-note pulse at the supplied tempo
+ *
+ *  note that note_type is currently ignored in this function. see minute_at_tempo().
+ *
+ *  for constant tempi, this is anaologous to pulse_at_minute().
 */
 double
 */
 double
-TempoSection::tempo_at_pulse (const double& p) const
+TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
 {
 {
-
-       if (_type == Constant || _c_func == 0.0) {
-               return pulses_per_minute();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
+       if (constant) {
+               return ((m - minute()) * pulses_per_minute()) + pulse();
        }
        }
-       double const ppm = pulse_tempo_at_pulse (p - pulse());
-       return ppm;
+
+       return _pulse_at_tempo (ntpm) + pulse();
 }
 
 }
 
-/** 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.
+/** returns the whole-note pulse at the supplied session-relative minute.
 */
 double
 */
 double
-TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::pulse_at_minute (const double& m) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
-               return  pulses;
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
+       if (constant) {
+               return ((m - minute()) * pulses_per_minute()) + pulse();
        }
        }
-       return pulse_at_pulse_tempo (ppm) + pulse();
+
+       return _pulse_at_time (m - minute()) + pulse();
 }
 
 }
 
-/** returns the zero-based pulse (relative to session origin)
-   where the zero-based frame (relative to session)
-   lies.
+/** returns the session-relative minute at the supplied whole-note pulse.
 */
 double
 */
 double
-TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::minute_at_pulse (const double& p) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return ((p - pulse()) / pulses_per_minute()) + minute();
        }
 
        }
 
-       return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
+       return _time_at_pulse (p - pulse()) + minute();
 }
 
 }
 
-/** returns the zero-based frame (relative to session start frame)
-   where the zero-based pulse (relative to session start)
-   falls.
+/** returns thw whole-note pulse at session frame position f.
+ *  @param f the frame position.
+ *  @return the position in whole-note pulses corresponding to f
+ *
+ *  for use with musical units whose granularity is coarser than frames (e.g. ticks)
 */
 */
+double
+TempoSection::pulse_at_frame (const framepos_t& f) const
+{
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
+       if (constant) {
+               return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
+       }
+
+       return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
+}
 
 framepos_t
 
 framepos_t
-TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
+TempoSection::frame_at_pulse (const double& p) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
        }
 
        }
 
-       return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
+       return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
 }
 
 /*
 }
 
 /*
@@ -362,98 +448,99 @@ https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Te
 
 */
 
 
 */
 
-/*
-  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.
+/** compute this ramp's function constant from some tempo-pulse point
+ * @param end_npm end tempo (in note types per minute)
+ * @param end_pulse duration (pulses into global start) of some other position.
+ * @return the calculated function constant
 */
 double
 */
 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 / 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 (whole pulses/min.) and distance (in frames) from session origin */
-double
-TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
+TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
 {
 {
-       return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
-}
+       if (note_types_per_minute() == end_npm || type() == Constant) {
+               return 0.0;
+       }
 
 
-framepos_t
-TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
-{
-       return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
+       double const log_tempo_ratio = log (end_npm / note_types_per_minute());
+       return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
 }
 
 }
 
+/** compute the function constant from some tempo-time point.
+ * @param end_npm tempo (note types/min.)
+ * @param end_minute distance (in minutes) from session origin
+ * @return the calculated function constant
+*/
 double
 double
-TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
+TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
 {
 {
-       return (frame / (double) frame_rate) / 60.0;
+       if (note_types_per_minute() == end_npm || type() == Constant) {
+               return 0.0;
+       }
+
+       return c_func (end_npm, end_minute - minute());
 }
 
 /* position function */
 double
 }
 
 /* position function */
 double
-TempoSection::a_func (double end_ppm, double c_func) const
+TempoSection::a_func (double end_npm, double c) const
 {
 {
-       return log (end_ppm / pulses_per_minute()) /  c_func;
+       return log (end_npm / note_types_per_minute()) / c;
 }
 
 /*function constant*/
 double
 }
 
 /*function constant*/
 double
-TempoSection::c_func (double end_ppm, double end_time) const
+TempoSection::c_func (double end_npm, double end_time) const
 {
 {
-       return log (end_ppm / pulses_per_minute()) /  end_time;
+       return log (end_npm / note_types_per_minute()) / end_time;
 }
 
 }
 
-/* tempo in ppm at time in minutes */
+/* tempo in note types per minute at time in minutes */
 double
 double
-TempoSection::pulse_tempo_at_time (const double& time) const
+TempoSection::_tempo_at_time (const double& time) const
 {
 {
-       return exp (_c_func * time) * pulses_per_minute();
+       return exp (_c * time) * note_types_per_minute();
 }
 
 }
 
-/* time in minutes at tempo in ppm */
+/* time in minutes at tempo in note types per minute */
 double
 double
-TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
+TempoSection::_time_at_tempo (const double& npm) const
 {
 {
-       return log (pulse_tempo / pulses_per_minute()) / _c_func;
+       return log (npm / note_types_per_minute()) / _c;
 }
 
 }
 
-/* tick at tempo in ppm */
+/* pulse at tempo in note types per minute */
 double
 double
-TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
+TempoSection::_pulse_at_tempo (const double& npm) const
 {
 {
-       return (pulse_tempo - pulses_per_minute()) / _c_func;
+       return ((npm - note_types_per_minute()) / _c) / _note_type;
 }
 
 }
 
-/* tempo in ppm at tick */
+/* tempo in note types per minute at pulse */
 double
 double
-TempoSection::pulse_tempo_at_pulse (const double& pulse) const
+TempoSection::_tempo_at_pulse (const double& pulse) const
 {
 {
-       return (pulse * _c_func) + pulses_per_minute();
+       return (pulse * _note_type * _c) + note_types_per_minute();
 }
 
 /* pulse at time in minutes */
 double
 }
 
 /* pulse at time in minutes */
 double
-TempoSection::pulse_at_time (const double& time) const
+TempoSection::_pulse_at_time (const double& time) const
 {
 {
-       return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
+       return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
 }
 
 /* time in minutes at pulse */
 double
 }
 
 /* time in minutes at pulse */
 double
-TempoSection::time_at_pulse (const double& pulse) const
+TempoSection::_time_at_pulse (const double& pulse) const
 {
 {
-       return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
+       return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
 }
 
 /***********************************************************************/
 
 const string MeterSection::xml_state_node_name = "Meter";
 
 }
 
 /***********************************************************************/
 
 const string MeterSection::xml_state_node_name = "Meter";
 
-MeterSection::MeterSection (const XMLNode& node)
-       : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
+MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
+       : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
 {
        XMLProperty const * prop;
        LocaleGuard lg;
 {
        XMLProperty const * prop;
        LocaleGuard lg;
@@ -507,8 +594,9 @@ MeterSection::MeterSection (const XMLNode& node)
        if ((prop = node.property ("frame")) != 0) {
                if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
                        error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
        if ((prop = node.property ("frame")) != 0) {
                if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
                        error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
+                       throw failed_constructor();
                } else {
                } else {
-                       set_frame (frame);
+                       set_minute (minute_at_frame (frame));
                }
        }
 
                }
        }
 
@@ -539,11 +627,11 @@ MeterSection::MeterSection (const XMLNode& node)
                throw failed_constructor();
        }
 
                throw failed_constructor();
        }
 
-       set_movable (string_is_affirmative (prop->value()));
+       set_initial (!string_is_affirmative (prop->value()));
 
        if ((prop = node.property ("lock-style")) == 0) {
                warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
 
        if ((prop = node.property ("lock-style")) == 0) {
                warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
-               if (movable()) {
+               if (!initial()) {
                        set_position_lock_style (MusicTime);
                } else {
                        set_position_lock_style (AudioTime);
                        set_position_lock_style (MusicTime);
                } else {
                        set_position_lock_style (AudioTime);
@@ -576,7 +664,7 @@ MeterSection::get_state() const
        root->add_property ("lock-style", enum_2_string (position_lock_style()));
        snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
        root->add_property ("divisions-per-bar", buf);
        root->add_property ("lock-style", enum_2_string (position_lock_style()));
        snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
        root->add_property ("divisions-per-bar", buf);
-       snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
+       snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
        root->add_property ("movable", buf);
 
        return *root;
        root->add_property ("movable", buf);
 
        return *root;
@@ -586,47 +674,38 @@ MeterSection::get_state() const
 /*
   Tempo Map Overview
 
 /*
   Tempo Map Overview
 
-  The Shaggs - Things I Wonder
-  https://www.youtube.com/watch?v=9wQK6zMJOoQ
-
-  Tempo is the rate of the musical pulse.
-  Meter divides pulse into measures and beats.
-
-  TempoSection - provides pulse in the form of beats_per_minute() - the number of quarter notes in one minute.
-  Note that 'beats' in Tempo::beats_per_minute() are quarter notes (pulse based). In the rest of tempo map,
-  'beat' usually refers to accumulated BBT beats (pulse and meter based).
-
-  MeterSecion - divides pulse into measures (via divisions_per_bar) and beats (via note_divisor).
+  Tempo determines the rate of musical pulse determined by its components
+        note types per minute - the rate per minute of the whole note divisor _note_type
+       note type             - the division of whole notes (pulses) which occur at the rate of note types per minute.
+  Meter divides the musical pulse into measures and beats according to its components
+        divisions_per_bar
+       note_divisor
+
+  TempoSection - translates between time, musical pulse and tempo.
+        has a musical location in whole notes (pulses).
+       has a time location in minutes.
+       Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
+       (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
+
+  MeterSection - translates between BBT, meter-based beat and musical pulse.
+        has a musical location in whole notes (pulses)
+       has a musical location in meter-based beats
+       has a musical location in BBT time
+       has a time location expressed in minutes.
 
 
-  Both tempo and meter have a pulse position and a frame position.
-  Meters also have a beat position, which is always 0.0 for the first one.
   TempoSection and MeterSection may be locked to either audio or music (position lock style).
   TempoSection and MeterSection may be locked to either audio or music (position lock style).
-  The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
+  The lock style determines the location type to be kept as a reference when location is recalculated.
 
 
-  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.
+  The first tempo and meter are special. they must move together, and are locked to audio.
+  Audio locked tempi which lie before the first meter are made inactive.
 
 
-  With tempo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
-  referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
-  sb->beat() - meter->beat() / meter->note_divisor().
-  Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
-  two meters is of course
-  (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
+  Recomputing the map is the process where the 'missing' location types are calculated.
+        We construct the tempo map by first using the locked location type of each section
+       to determine non-locked location types (pulse or minute position).
+        We then use this map to find the pulse or minute position of each meter (again depending on lock style).
 
 
-  Beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
-  Beat to frame conversion of course requires the use of meter and tempo.
-
-  Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
-  Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
-
-  Recomputing the map is the process where the 'missing' position
-  (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
-  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 tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
-
-  Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
-  and querying its appropriate meter/tempo.
+  Having done this, we can now traverse the Metrics list by pulse or minute
+  to query its relevant meter/tempo.
 
   It is important to keep the _metrics in an order that makes sense.
   Because ramped MusicTime and AudioTime tempos can interact with each other,
 
   It is important to keep the _metrics in an order that makes sense.
   Because ramped MusicTime and AudioTime tempos can interact with each other,
@@ -663,11 +742,14 @@ MeterSection::get_state() const
   beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
 
   Doing the second one will result in a beat distance error of up to 0.5 audio samples.
   beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
 
   Doing the second one will result in a beat distance error of up to 0.5 audio samples.
-  So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
-  For audio-locked objects, use frame position to calculate beat position.
+  frames_between_quarter_notes () eliminats this effect when determining time duration
+  from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
+
+  The above pointless example could instead do:
+  beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
 
 
-  The above pointless example would then do:
-  beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
+  The Shaggs - Things I Wonder
+  https://www.youtube.com/watch?v=9wQK6zMJOoQ
 
 */
 struct MetricSectionSorter {
 
 */
 struct MetricSectionSorter {
@@ -687,11 +769,13 @@ TempoMap::TempoMap (framecnt_t fr)
        _frame_rate = fr;
        BBT_Time start (1, 1, 0);
 
        _frame_rate = fr;
        BBT_Time start (1, 1, 0);
 
-       TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
-       MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
+       TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
+       MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
 
 
-       t->set_movable (false);
-       m->set_movable (false);
+       t->set_initial (true);
+       t->set_locked_to_meter (true);
+
+       m->set_initial (true);
 
        /* note: frame time is correct (zero) for both of these */
 
 
        /* note: frame time is correct (zero) for both of these */
 
@@ -700,6 +784,55 @@ TempoMap::TempoMap (framecnt_t fr)
 
 }
 
 
 }
 
+TempoMap::TempoMap (TempoMap const & other)
+{
+       _frame_rate = other._frame_rate;
+       for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
+               TempoSection* ts = dynamic_cast<TempoSection*> (*m);
+               MeterSection* ms = dynamic_cast<MeterSection*> (*m);
+
+               if (ts) {
+                       TempoSection* new_section = new TempoSection (*ts);
+                       _metrics.push_back (new_section);
+               } else {
+                       MeterSection* new_section = new MeterSection (*ms);
+                       _metrics.push_back (new_section);
+               }
+       }
+}
+
+TempoMap&
+TempoMap::operator= (TempoMap const & other)
+{
+       if (&other != this) {
+               _frame_rate = other._frame_rate;
+
+               Metrics::const_iterator d = _metrics.begin();
+               while (d != _metrics.end()) {
+                       delete (*d);
+                       ++d;
+               }
+               _metrics.clear();
+
+               for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
+                       TempoSection* ts = dynamic_cast<TempoSection*> (*m);
+                       MeterSection* ms = dynamic_cast<MeterSection*> (*m);
+
+                       if (ts) {
+                               TempoSection* new_section = new TempoSection (*ts);
+                               _metrics.push_back (new_section);
+                       } else {
+                               MeterSection* new_section = new MeterSection (*ms);
+                               _metrics.push_back (new_section);
+                       }
+               }
+       }
+
+       PropertyChanged (PropertyChange());
+
+       return *this;
+}
+
 TempoMap::~TempoMap ()
 {
        Metrics::const_iterator d = _metrics.begin();
 TempoMap::~TempoMap ()
 {
        Metrics::const_iterator d = _metrics.begin();
@@ -710,6 +843,18 @@ TempoMap::~TempoMap ()
        _metrics.clear();
 }
 
        _metrics.clear();
 }
 
+framepos_t
+TempoMap::frame_at_minute (const double time) const
+{
+       return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
+}
+
+double
+TempoMap::minute_at_frame (const framepos_t frame) const
+{
+       return (frame / (double) _frame_rate) / 60.0;
+}
+
 void
 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 {
 void
 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 {
@@ -737,7 +882,7 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<TempoSection*> (*i) != 0) {
                        if (tempo.frame() == (*i)->frame()) {
        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<TempoSection*> (*i) != 0) {
                        if (tempo.frame() == (*i)->frame()) {
-                               if ((*i)->movable()) {
+                               if (!(*i)->initial()) {
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
@@ -789,7 +934,7 @@ TempoMap::remove_meter_locked (const MeterSection& meter)
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<MeterSection*> (*i) != 0) {
                        if (meter.frame() == (*i)->frame()) {
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<MeterSection*> (*i) != 0) {
                        if (meter.frame() == (*i)->frame()) {
-                               if ((*i)->movable()) {
+                               if (!(*i)->initial()) {
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
@@ -843,7 +988,7 @@ TempoMap::do_insert (MetricSection* section)
                        bool const ipm = insert_tempo->position_lock_style() == MusicTime;
                        if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && 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()) {
+                               if (tempo->initial()) {
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
@@ -852,10 +997,6 @@ TempoMap::do_insert (MetricSection* section)
 
                                        *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        (*i)->set_position_lock_style (AudioTime);
 
                                        *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        (*i)->set_position_lock_style (AudioTime);
-                                       TempoSection* t;
-                                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                                               t->set_type (insert_tempo->type());
-                                       }
                                        need_add = false;
                                } else {
                                        delete (*i);
                                        need_add = false;
                                } else {
                                        delete (*i);
@@ -872,7 +1013,7 @@ TempoMap::do_insert (MetricSection* section)
 
                        if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
 
 
                        if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
 
-                               if (!meter->movable()) {
+                               if (meter->initial()) {
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
@@ -902,15 +1043,24 @@ TempoMap::do_insert (MetricSection* section)
                MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
                TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
                Metrics::iterator i;
                MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
                TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
                Metrics::iterator i;
+
                if (insert_meter) {
                if (insert_meter) {
+                       TempoSection* prev_t = 0;
+
                        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                                MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
                        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                                MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+                               bool const ipm = insert_meter->position_lock_style() == MusicTime;
 
                                if (meter) {
 
                                if (meter) {
-                                       bool const ipm = insert_meter->position_lock_style() == MusicTime;
                                        if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
                                                break;
                                        }
                                        if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
                                                break;
                                        }
+                               } else {
+                                       if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
+                                               break;
+                                       }
+
+                                       prev_t = dynamic_cast<TempoSection*> (*i);
                                }
                        }
                } else if (insert_tempo) {
                                }
                        }
                } else if (insert_tempo) {
@@ -919,7 +1069,9 @@ TempoMap::do_insert (MetricSection* section)
 
                                if (tempo) {
                                        bool const ipm = insert_tempo->position_lock_style() == MusicTime;
 
                                if (tempo) {
                                        bool const ipm = insert_tempo->position_lock_style() == MusicTime;
-                                       if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
+                                       const bool lm = insert_tempo->locked_to_meter();
+                                       if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
+                                           || (lm && tempo->pulse() > insert_tempo->pulse())) {
                                                break;
                                        }
                                }
                                                break;
                                        }
                                }
@@ -927,19 +1079,42 @@ TempoMap::do_insert (MetricSection* section)
                }
 
                _metrics.insert (i, section);
                }
 
                _metrics.insert (i, section);
-               //dump (_metrics, std::cout);
+               //dump (std::cout);
        }
 }
        }
 }
-
+/* user supplies the exact pulse if pls == MusicTime */
 TempoSection*
 TempoSection*
-TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
+TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
 {
 {
+       if (tempo.note_types_per_minute() <= 0.0) {
+               warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
+               return 0;
+       }
+
        TempoSection* ts = 0;
        TempoSection* ts = 0;
+       TempoSection* prev_tempo = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
-       }
+               ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
+               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
 
+                       if ((*i)->is_tempo()) {
+                               TempoSection* const this_t = static_cast<TempoSection*> (*i);
+
+                               bool const ipm = ts->position_lock_style() == MusicTime;
+                               bool const lm = ts->locked_to_meter();
+                               if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
+                                   || (lm && this_t->pulse() == ts->pulse())) {
+                                       if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
+                                               prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
+                                       }
+                                       break;
+                               }
+                               prev_tempo = this_t;
+                       }
+               }
+               recompute_map (_metrics);
+       }
 
        PropertyChanged (PropertyChange ());
 
 
        PropertyChanged (PropertyChange ());
 
@@ -947,60 +1122,93 @@ TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t&
 }
 
 void
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
+TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
 {
 {
-       const bool locked_to_meter = ts.locked_to_meter();
+       if (tempo.note_types_per_minute() <= 0.0) {
+               warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
+               return;
+       }
+
+       bool const locked_to_meter = ts.locked_to_meter();
+       bool const ts_clamped = ts.clamped();
+       TempoSection* new_ts = 0;
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
-               if (ts.frame() != first.frame()) {
-                       remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
+               if (!ts.initial()) {
+                       if (locked_to_meter) {
+                               {
+                                       /* cannot move a meter-locked tempo section */
+                                       *static_cast<Tempo*>(&ts) = tempo;
+                                       recompute_map (_metrics);
+                               }
+                       } else {
+                               remove_tempo_locked (ts);
+                               new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
+                               new_ts->set_clamped (ts_clamped);
+
+                               if (new_ts && new_ts->type() == TempoSection::Constant) {
+                                       new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
+                               } else {
+                                       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+
+                                               if ((*i)->is_tempo()) {
+                                                       TempoSection* const this_t = static_cast<TempoSection*> (*i);
+
+                                                       bool const ipm = new_ts->position_lock_style() == MusicTime;
+                                                       bool const lm = new_ts->locked_to_meter();
+                                                       if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
+                                                           || (lm && this_t->pulse() > new_ts->pulse())) {
+                                                               new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
+
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
                } else {
                } else {
-                       first.set_type (type);
                        first.set_pulse (0.0);
                        first.set_pulse (0.0);
-                       first.set_frame (frame);
+                       first.set_minute (minute_at_frame (frame));
                        first.set_position_lock_style (AudioTime);
                        first.set_position_lock_style (AudioTime);
+                       first.set_locked_to_meter (true);
+                       first.set_clamped (ts_clamped);
                        {
                                /* cannot move the first tempo section */
                                *static_cast<Tempo*>(&first) = tempo;
                        {
                                /* cannot move the first tempo section */
                                *static_cast<Tempo*>(&first) = tempo;
-                               recompute_map (_metrics);
                        }
                }
                        }
                }
+               recompute_map (_metrics);
        }
 
        PropertyChanged (PropertyChange ());
 }
 
 TempoSection*
        }
 
        PropertyChanged (PropertyChange ());
 }
 
 TempoSection*
-TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
-                           , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
+                           , PositionLockStyle pls, bool recompute, bool locked_to_meter)
 {
 {
-       TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
+       TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
        t->set_locked_to_meter (locked_to_meter);
        t->set_locked_to_meter (locked_to_meter);
-       bool solved = false;
 
        do_insert (t);
 
        if (recompute) {
                if (pls == AudioTime) {
 
        do_insert (t);
 
        if (recompute) {
                if (pls == AudioTime) {
-                       solved = solve_map_frame (_metrics, t, t->frame());
+                       solve_map_minute (_metrics, t, t->minute());
                } else {
                } else {
-                       solved = solve_map_pulse (_metrics, t, t->pulse());
+                       solve_map_pulse (_metrics, t, t->pulse());
                }
                recompute_meters (_metrics);
        }
 
                }
                recompute_meters (_metrics);
        }
 
-       if (!solved && recompute) {
-               recompute_map (_metrics);
-       }
-
        return t;
 }
 
 MeterSection*
        return t;
 }
 
 MeterSection*
-TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
+TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
 {
        MeterSection* m = 0;
        {
 {
        MeterSection* m = 0;
        {
@@ -1011,7 +1219,7 @@ TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (_metrics, std::cerr);
+               dump (std::cerr);
        }
 #endif
 
        }
 #endif
 
@@ -1020,13 +1228,13 @@ TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT
 }
 
 void
 }
 
 void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                const double beat = beat_at_bbt_locked (_metrics, where);
 
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                const double beat = beat_at_bbt_locked (_metrics, where);
 
-               if (ms.movable()) {
+               if (!ms.initial()) {
                        remove_meter_locked (ms);
                        add_meter_locked (meter, beat, where, frame, pls, true);
                } else {
                        remove_meter_locked (ms);
                        add_meter_locked (meter, beat, where, frame, pls, true);
                } else {
@@ -1036,10 +1244,11 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
                        *static_cast<Meter*>(&first) = meter;
                        first.set_position_lock_style (AudioTime);
                        first.set_pulse (0.0);
                        *static_cast<Meter*>(&first) = meter;
                        first.set_position_lock_style (AudioTime);
                        first.set_pulse (0.0);
-                       first.set_frame (frame);
+                       first.set_minute (minute_at_frame (frame));
                        pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
                        first.set_beat (beat);
                        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_minute (first.minute());
+                       first_t.set_locked_to_meter (true);
                        first_t.set_pulse (0.0);
                        first_t.set_position_lock_style (AudioTime);
                        recompute_map (_metrics);
                        first_t.set_pulse (0.0);
                        first_t.set_position_lock_style (AudioTime);
                        recompute_map (_metrics);
@@ -1052,13 +1261,14 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 MeterSection*
 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
 {
 MeterSection*
 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
 {
-       const MeterSection& prev_m = meter_section_at_frame_locked  (_metrics, frame - 1);
+       const MeterSection& prev_m = meter_section_at_minute_locked  (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
        const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
        const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
+       const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
        TempoSection* mlt = 0;
 
        if (pls == AudioTime) {
                /* add meter-locked tempo */
        TempoSection* mlt = 0;
 
        if (pls == AudioTime) {
                /* add meter-locked tempo */
-               mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse,  frame, TempoSection::Ramp, AudioTime, true, true);
+               mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
 
                if (!mlt) {
                        return 0;
 
                if (!mlt) {
                        return 0;
@@ -1066,7 +1276,8 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe
 
        }
 
 
        }
 
-       MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
+       MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
+
        bool solved = false;
 
        do_insert (new_meter);
        bool solved = false;
 
        do_insert (new_meter);
@@ -1074,7 +1285,13 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe
        if (recompute) {
 
                if (pls == AudioTime) {
        if (recompute) {
 
                if (pls == AudioTime) {
-                       solved = solve_map_frame (_metrics, new_meter, frame);
+                       solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
+                       /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
+                          fudge frame so that the meter ends up at its BBT position instead.
+                       */
+                       if (!solved) {
+                               solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
+                       }
                } else {
                        solved = solve_map_bbt (_metrics, new_meter, where);
                        /* required due to resetting the pulse of meter-locked tempi above.
                } else {
                        solved = solve_map_bbt (_metrics, new_meter, where);
                        /* required due to resetting the pulse of meter-locked tempi above.
@@ -1097,9 +1314,9 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe
 }
 
 void
 }
 
 void
-TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
+TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
 {
 {
-       Tempo newtempo (beats_per_minute, note_type);
+       Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
        TempoSection* t;
 
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
        TempoSection* t;
 
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
@@ -1119,9 +1336,9 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 }
 
 void
 }
 
 void
-TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
+TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
 {
 {
-       Tempo newtempo (beats_per_minute, note_type);
+       Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
 
        TempoSection* prev;
        TempoSection* first;
 
        TempoSection* prev;
        TempoSection* first;
@@ -1216,7 +1433,7 @@ TempoMap::first_tempo () const
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                return *t;
                        }
                }
                                return *t;
                        }
                }
@@ -1237,7 +1454,7 @@ TempoMap::first_tempo ()
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                return *t;
                        }
                }
                                return *t;
                        }
                }
@@ -1260,7 +1477,7 @@ TempoMap::recompute_tempi (Metrics& metrics)
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                if (!prev_t) {
                                        t->set_pulse (0.0);
                                        prev_t = t;
                                if (!prev_t) {
                                        t->set_pulse (0.0);
                                        prev_t = t;
@@ -1269,21 +1486,22 @@ TempoMap::recompute_tempi (Metrics& metrics)
                        }
                        if (prev_t) {
                                if (t->position_lock_style() == AudioTime) {
                        }
                        if (prev_t) {
                                if (t->position_lock_style() == AudioTime) {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
                                        }
 
                                } else {
                                        }
 
                                } 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_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
+                                       t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
 
                                }
                        }
                        prev_t = t;
                }
        }
 
                                }
                        }
                        prev_t = t;
                }
        }
-       prev_t->set_c_func (0.0);
+       assert (prev_t);
+       prev_t->set_c (0.0);
 }
 
 /* tempos must be positioned correctly.
 }
 
 /* tempos must be positioned correctly.
@@ -1308,7 +1526,7 @@ TempoMap::recompute_meters (Metrics& metrics)
                                        TempoSection* t;
                                        if ((*ii)->is_tempo()) {
                                                t = static_cast<TempoSection*> (*ii);
                                        TempoSection* t;
                                        if ((*ii)->is_tempo()) {
                                                t = static_cast<TempoSection*> (*ii);
-                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
+                                               if (t->locked_to_meter() && t->frame() == meter->frame()) {
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
@@ -1316,14 +1534,16 @@ TempoMap::recompute_meters (Metrics& metrics)
                                }
 
                                if (prev_m) {
                                }
 
                                if (prev_m) {
-                                       const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+                                       double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
                                        if (beats + prev_m->beat() != meter->beat()) {
                                                /* reordering caused a bbt change */
                                        if (beats + prev_m->beat() != meter->beat()) {
                                                /* reordering caused a bbt change */
+
+                                               beats = meter->beat() - prev_m->beat();
                                                b_bbt = make_pair (beats + prev_m->beat()
                                                                   , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
                                                pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
 
                                                b_bbt = make_pair (beats + prev_m->beat()
                                                                   , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
                                                pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
 
-                                       } else if (meter->movable()) {
+                                       } else if (!meter->initial()) {
                                                b_bbt = make_pair (meter->beat(), meter->bbt());
                                                pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                        }
                                                b_bbt = make_pair (meter->beat(), meter->bbt());
                                                pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                        }
@@ -1358,7 +1578,7 @@ TempoMap::recompute_meters (Metrics& metrics)
 
                                meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
 
                                meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
-                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+                               meter->set_minute (minute_at_pulse_locked (metrics, pulse));
                        }
 
                        prev_m = meter;
                        }
 
                        prev_m = meter;
@@ -1371,15 +1591,6 @@ TempoMap::recompute_map (Metrics& metrics, framepos_t end)
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
 {
        /* 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) {
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
        if (end == 0) {
@@ -1452,9 +1663,10 @@ TempoMap::metric_at (BBT_Time bbt) const
        return m;
 }
 
        return m;
 }
 
-/** Returns the beat duration corresponding to the supplied frame, possibly returning a negative value.
+/** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
  * @param frame The session frame position.
  * @return The beat duration according to the tempo map at the supplied frame.
  * @param frame The session frame position.
  * @return The beat duration according to the tempo map at the supplied frame.
+ *
  * If the supplied frame lies before the first meter, the returned beat duration will be negative.
  * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
  *
  * If the supplied frame lies before the first meter, the returned beat duration will be negative.
  * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
  *
@@ -1464,20 +1676,21 @@ double
 TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_frame_locked (_metrics, frame);
+
+       return beat_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 /* This function uses both tempo and meter.*/
 double
 }
 
 /* This function uses both tempo and meter.*/
 double
-TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
 {
-       const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
+       const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
-                       if (prev_m && (*i)->frame() > frame) {
+                       if (prev_m && (*i)->minute() > minute) {
                                next_m = static_cast<MeterSection*> (*i);
                                break;
                        }
                                next_m = static_cast<MeterSection*> (*i);
                                break;
                        }
@@ -1485,7 +1698,7 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
                }
        }
 
                }
        }
 
-       const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+       const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
 
        /* audio locked meters fake their beat */
        if (next_m && next_m->beat() < beat) {
 
        /* audio locked meters fake their beat */
        if (next_m && next_m->beat() < beat) {
@@ -1495,16 +1708,23 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
        return beat;
 }
 
        return beat;
 }
 
+/** Returns the frame corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
+ *
+ * This function uses both tempo and meter.
+ */
 framepos_t
 TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 framepos_t
 TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_beat_locked (_metrics, beat);
+
+       return frame_at_minute (minute_at_beat_locked (_metrics, beat));
 }
 
 /* meter & tempo section based */
 }
 
 /* meter & tempo section based */
-framepos_t
-TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+double
+TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
        MeterSection* prev_m = 0;
        TempoSection* prev_t = 0;
 {
        MeterSection* prev_m = 0;
        TempoSection* prev_t = 0;
@@ -1520,12 +1740,18 @@ TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) cons
                        prev_m = m;
                }
        }
                        prev_m = m;
                }
        }
+       assert (prev_m);
 
        TempoSection* t;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
 
        TempoSection* t;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
                        if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
                                break;
                        }
                        if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
                                break;
                        }
@@ -1533,44 +1759,46 @@ TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) cons
                }
 
        }
                }
 
        }
+       assert (prev_t);
 
 
-       return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
+       return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
 }
 
 }
 
+/** Returns a Tempo corresponding to the supplied frame position.
+ * @param frame The audio frame.
+ * @return a Tempo according to the tempo map at the supplied frame.
+ *
+ */
 Tempo
 TempoMap::tempo_at_frame (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 Tempo
 TempoMap::tempo_at_frame (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_at_frame_locked (_metrics, frame);
+
+       return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 Tempo
 }
 
 Tempo
-TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
        TempoSection* prev_t = 0;
 
        TempoSection* t;
 
 {
        TempoSection* prev_t = 0;
 
        TempoSection* t;
 
-       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
                        if (!t->active()) {
                                continue;
                        }
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
                        if (!t->active()) {
                                continue;
                        }
-                       if ((prev_t) && t->frame() > frame) {
+                       if ((prev_t) && t->minute() > minute) {
                                /* t is the section past frame */
                                /* t is the section past frame */
-                               const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * 4.0;
-                               const Tempo ret_tempo (ret_bpm, prev_t->note_type());
-                               return ret_tempo;
+                               return prev_t->tempo_at_minute (minute);
                        }
                        prev_t = t;
                }
        }
 
                        }
                        prev_t = t;
                }
        }
 
-       const double ret = prev_t->beats_per_minute();
-       const Tempo ret_tempo (ret, prev_t->note_type ());
-
-       return ret_tempo;
+       return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
 }
 
 /** returns the frame at which the supplied tempo occurs, or
 }
 
 /** returns the frame at which the supplied tempo occurs, or
@@ -1582,19 +1810,83 @@ framepos_t
 TempoMap::frame_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::frame_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_tempo_locked (_metrics, tempo);
+
+       return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
 }
 
 }
 
+double
+TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
+{
+       TempoSection* prev_t = 0;
+       const double tempo_bpm = tempo.note_types_per_minute();
 
 
-framepos_t
-TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
+
+
+                       if (t->note_types_per_minute() == tempo_bpm) {
+                               return t->minute();
+                       }
+
+                       if (prev_t) {
+                               const double prev_t_bpm = prev_t->note_types_per_minute();
+                               const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
+                               if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
+                                   || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
+                                   || (prev_t_end_bpm == tempo_bpm)) {
+
+                                       return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
+                               }
+                       }
+                       prev_t = t;
+               }
+       }
+
+       return prev_t->minute();
+}
+
+Tempo
+TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       TempoSection* prev_t = 0;
+
+       TempoSection* t;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if ((prev_t) && t->pulse() > pulse) {
+                               /* t is the section past frame */
+                               return prev_t->tempo_at_pulse (pulse);
+                       }
+                       prev_t = t;
+               }
+       }
+
+       return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
+}
+
+double
+TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
 {
        TempoSection* prev_t = 0;
 {
        TempoSection* prev_t = 0;
-       const double tempo_ppm = tempo.beats_per_minute() / 4.0;
+       const double tempo_bpm = tempo.note_types_per_minute();
 
        Metrics::const_iterator i;
 
 
        Metrics::const_iterator i;
 
-       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
                TempoSection* t;
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
@@ -1603,47 +1895,60 @@ TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con
                                continue;
                        }
 
                                continue;
                        }
 
-                       const double t_ppm = t->beats_per_minute() / 4.0;
+                       const double t_bpm = t->note_types_per_minute();
 
 
-                       if (t_ppm == tempo_ppm) {
-                               return t->frame();
+                       if (t_bpm == tempo_bpm) {
+                               return t->pulse();
                        }
 
                        if (prev_t) {
                        }
 
                        if (prev_t) {
-                               const double prev_t_ppm = prev_t->beats_per_minute() / 4.0;
+                               const double prev_t_bpm = prev_t->note_types_per_minute();
 
 
-                               if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
-                                       return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
+                               if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
+                                       return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
                                }
                        }
                        prev_t = t;
                }
        }
 
                                }
                        }
                        prev_t = t;
                }
        }
 
-       return prev_t->frame();
+       return prev_t->pulse();
 }
 
 }
 
-/** more precise than doing tempo_at_frame (frame_at_beat (b)),
- *  as there is no intermediate frame rounding.
+/** Returns a Tempo corresponding to the supplied position in quarter-note beats.
+ * @param qn the position in quarter note beats.
+ * @return the Tempo at the supplied quarter-note.
  */
 Tempo
  */
 Tempo
-TempoMap::tempo_at_beat (const double& beat) const
+TempoMap::tempo_at_quarter_note (const double& qn) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
-       const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
-       const double note_type = prev_t->note_type();
 
 
-       return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * 4.0, note_type);
+       return tempo_at_pulse_locked (_metrics, qn / 4.0);
 }
 
 }
 
+/** Returns the position in quarter-note beats corresponding to the supplied Tempo.
+ * @param tempo the tempo.
+ * @return the position in quarter-note beats where the map bpm
+ * is equal to that of the Tempo. currently ignores note_type.
+ */
 double
 double
-TempoMap::pulse_at_beat (const double& beat) const
+TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_beat_locked (_metrics, beat);
+
+       return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
 }
 
 }
 
+/** Returns the whole-note pulse corresponding to the supplied  BBT (meter-based) beat.
+ * @param metrics the list of metric sections used to calculate the pulse.
+ * @param beat The BBT (meter-based) beat.
+ * @return the whole-note pulse at the supplied BBT (meter-based) beat.
+ *
+ * a pulse or whole note is the base musical position of a MetricSection.
+ * it is equivalent to four quarter notes.
+ *
+ */
 double
 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
 double
 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
@@ -1652,13 +1957,14 @@ TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) cons
        return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
 }
 
        return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
 }
 
-double
-TempoMap::beat_at_pulse (const double& pulse) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_pulse_locked (_metrics, pulse);
-}
-
+/** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
+ * @param metrics the list of metric sections used to calculate the beat.
+ * @param pulse the whole-note pulse.
+ * @return the meter-based beat at the supplied whole-note pulse.
+ *
+ * a pulse or whole note is the base musical position of a MetricSection.
+ * it is equivalent to four quarter notes.
+ */
 double
 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
 double
 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
@@ -1669,28 +1975,20 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (prev_m && m->pulse() > pulse) {
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (prev_m && m->pulse() > pulse) {
-                               if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
-                                       break;
-                               }
+                               break;
                        }
                        prev_m = m;
                }
        }
                        }
                        prev_m = m;
                }
        }
+       assert (prev_m);
 
        double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
        return ret;
 }
 
 
        double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
        return ret;
 }
 
-double
-TempoMap::pulse_at_frame (const framepos_t& frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_frame_locked (_metrics, frame);
-}
-
 /* tempo section based */
 double
 /* tempo section based */
 double
-TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
        /* HOLD (at least) THE READER LOCK */
        TempoSection* prev_t = 0;
 {
        /* HOLD (at least) THE READER LOCK */
        TempoSection* prev_t = 0;
@@ -1702,9 +2000,9 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_t && t->frame() > frame) {
+                       if (prev_t && t->minute() > minute) {
                                /*the previous ts is the one containing the frame */
                                /*the previous ts is the one containing the frame */
-                               const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
+                               const double ret = prev_t->pulse_at_minute (minute);
                                /* audio locked section in new meter*/
                                if (t->pulse() < ret) {
                                        return t->pulse();
                                /* audio locked section in new meter*/
                                if (t->pulse() < ret) {
                                        return t->pulse();
@@ -1716,21 +2014,14 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame
        }
 
        /* treated as constant for this ts */
        }
 
        /* treated as constant for this ts */
-       const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
+       const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
 
        return pulses_in_section + prev_t->pulse();
 }
 
 
        return pulses_in_section + prev_t->pulse();
 }
 
-framepos_t
-TempoMap::frame_at_pulse (const double& pulse) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_pulse_locked (_metrics, pulse);
-}
-
 /* tempo section based */
 /* tempo section based */
-framepos_t
-TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+double
+TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
        /* HOLD THE READER LOCK */
 
 {
        /* HOLD THE READER LOCK */
 
@@ -1745,18 +2036,23 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co
                                continue;
                        }
                        if (prev_t && t->pulse() > pulse) {
                                continue;
                        }
                        if (prev_t && t->pulse() > pulse) {
-                               return prev_t->frame_at_pulse (pulse, _frame_rate);
+                               return prev_t->minute_at_pulse (pulse);
                        }
 
                        prev_t = t;
                }
        }
        /* must be treated as constant, irrespective of _type */
                        }
 
                        prev_t = t;
                }
        }
        /* must be treated as constant, irrespective of _type */
-       double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
+       double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
 
 
-       return (framecnt_t) floor (dtime) + prev_t->frame();
+       return dtime + prev_t->minute();
 }
 
 }
 
+/** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
+ * @param bbt The BBT time (meter-based).
+ * @return bbt The BBT beat (meter-based) at the supplied BBT time.
+ *
+ */
 double
 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
 {
 double
 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
 {
@@ -1797,11 +2093,16 @@ TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time&
        return ret;
 }
 
        return ret;
 }
 
+/** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The BBT time (meter-based) at the supplied meter-based beat.
+ *
+ */
 Timecode::BBT_Time
 Timecode::BBT_Time
-TempoMap::bbt_at_beat (const double& beats)
+TempoMap::bbt_at_beat (const double& beat)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_at_beat_locked (_metrics, beats);
+       return bbt_at_beat_locked (_metrics, beat);
 }
 
 Timecode::BBT_Time
 }
 
 Timecode::BBT_Time
@@ -1826,6 +2127,7 @@ TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
                        prev_m = m;
                }
        }
                        prev_m = m;
                }
        }
+       assert (prev_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 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());
@@ -1856,24 +2158,32 @@ TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
        return ret;
 }
 
        return ret;
 }
 
+/** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
+ * @param bbt The BBT time (meter-based).
+ * @return the quarter note beat at the supplied BBT time
+ *
+ * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
+ *
+ * while the input uses meter, the output does not.
+ */
 double
 double
-TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
+TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return pulse_at_bbt_locked (_metrics, bbt);
+       return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
 }
 
 double
 }
 
 double
-TempoMap::pulse_at_bbt_rt (const Timecode::BBT_Time& bbt)
+TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
 
        if (!lm.locked()) {
-               throw std::logic_error ("TempoMap::pulse_at_bbt_rt() could not lock tempo map");
+               throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
        }
 
        }
 
-       return pulse_at_bbt_locked (_metrics, bbt);
+       return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
 }
 
 double
 }
 
 double
@@ -1907,14 +2217,30 @@ TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time&
        return ret;
 }
 
        return ret;
 }
 
+/** Returns the BBT time corresponding to the supplied quarter-note beat.
+ * @param qn the quarter-note beat.
+ * @return The BBT time (meter-based) at the supplied meter-based beat.
+ *
+ * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
+ *
+ */
 Timecode::BBT_Time
 Timecode::BBT_Time
-TempoMap::bbt_at_pulse (const double& pulse)
+TempoMap::bbt_at_quarter_note (const double& qn)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return bbt_at_pulse_locked (_metrics, pulse);
+       return bbt_at_pulse_locked (_metrics, qn / 4.0);
 }
 
 }
 
+/** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
+ * @param metrics The list of metric sections used to determine the result.
+ * @param pulse The whole-note pulse.
+ * @return The BBT time at the supplied whole-note pulse.
+ *
+ * a pulse or whole note is the basic musical position of a MetricSection.
+ * it is equivalent to four quarter notes.
+ * while the output uses meter, the input does not.
+ */
 Timecode::BBT_Time
 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
 Timecode::BBT_Time
 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
@@ -1939,6 +2265,8 @@ TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) cons
                }
        }
 
                }
        }
 
+       assert (prev_m);
+
        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 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);
@@ -1968,6 +2296,11 @@ TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) cons
        return ret;
 }
 
        return ret;
 }
 
+/** Returns the BBT time corresponding to the supplied frame position.
+ * @param frame the position in audio samples.
+ * @return the BBT time at the frame position .
+ *
+ */
 BBT_Time
 TempoMap::bbt_at_frame (framepos_t frame)
 {
 BBT_Time
 TempoMap::bbt_at_frame (framepos_t frame)
 {
@@ -1976,30 +2309,37 @@ TempoMap::bbt_at_frame (framepos_t frame)
                bbt.bars = 1;
                bbt.beats = 1;
                bbt.ticks = 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;
+#ifndef NDEBUG
+               warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
+#endif
                return bbt;
        }
                return bbt;
        }
+
+       const double minute =  minute_at_frame (frame);
+
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return bbt_at_frame_locked (_metrics, frame);
+       return bbt_at_minute_locked (_metrics, minute);
 }
 
 BBT_Time
 TempoMap::bbt_at_frame_rt (framepos_t frame)
 {
 }
 
 BBT_Time
 TempoMap::bbt_at_frame_rt (framepos_t frame)
 {
+       const double minute =  minute_at_frame (frame);
+
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
        }
 
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
        }
 
-       return bbt_at_frame_locked (_metrics, frame);
+       return bbt_at_minute_locked (_metrics, minute);
 }
 
 Timecode::BBT_Time
 }
 
 Timecode::BBT_Time
-TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
 {
-       if (frame < 0) {
+       if (minute < 0) {
                BBT_Time bbt;
                bbt.bars = 1;
                bbt.beats = 1;
                BBT_Time bbt;
                bbt.bars = 1;
                bbt.beats = 1;
@@ -2007,7 +2347,7 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
                return bbt;
        }
 
                return bbt;
        }
 
-       const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
+       const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
@@ -2016,7 +2356,7 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
-                       if (prev_m && m->frame() > frame) {
+                       if (prev_m && m->minute() > minute) {
                                next_m = m;
                                break;
                        }
                                next_m = m;
                                break;
                        }
@@ -2024,10 +2364,10 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
                }
        }
 
                }
        }
 
-       double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+       double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
 
        /* handle frame before first meter */
 
        /* handle frame before first meter */
-       if (frame < prev_m->frame()) {
+       if (minute < prev_m->minute()) {
                beat = 0.0;
        }
        /* audio locked meters fake their beat */
                beat = 0.0;
        }
        /* audio locked meters fake their beat */
@@ -2066,120 +2406,203 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
        return ret;
 }
 
        return ret;
 }
 
+/** Returns the frame position corresponding to the supplied BBT time.
+ * @param bbt the position in BBT time.
+ * @return the frame position at bbt.
+ *
+ */
 framepos_t
 TempoMap::frame_at_bbt (const BBT_Time& bbt)
 {
        if (bbt.bars < 1) {
 framepos_t
 TempoMap::frame_at_bbt (const BBT_Time& bbt)
 {
        if (bbt.bars < 1) {
+#ifndef NDEBUG
                warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
                warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
+#endif
                return 0;
        }
 
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
                return 0;
        }
 
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
-       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       return frame_at_bbt_locked (_metrics, bbt);
+       double minute;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               minute = minute_at_bbt_locked (_metrics, bbt);
+       }
+
+       return frame_at_minute (minute);
 }
 
 /* meter & tempo section based */
 }
 
 /* meter & tempo section based */
-framepos_t
-TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
+double
+TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
 {
        /* HOLD THE READER LOCK */
 
 {
        /* HOLD THE READER LOCK */
 
-       const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
+       const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
        return ret;
 }
 
 /**
        return ret;
 }
 
 /**
- * Returns the distance from 0 in quarter pulses at the supplied frame.
+ * Returns the quarter-note beat position corresponding to the supplied frame.
  *
  *
- * Plugin APIs don't count ticks in the same way PROGRAM_NAME does.
- * We use ticks per beat whereas the rest of the world uses ticks per quarter note.
- * This is more or less the VST's ppqPos (a scalar you use to obtain tick position
- * in whatever ppqn you're using).
+ * @param frame the position in frames.
+ * @return The quarter-note position of the supplied frame. Ignores meter.
  *
  *
- * @param frame The distance in frames relative to session 0 whose quarter note distance you would like.
- * @return The quarter note (quarter pulse) distance from session 0 to the supplied frame. Ignores meter.
 */
 */
-
 double
 double
-TempoMap::quarter_note_at_frame (const framepos_t frame)
+TempoMap::quarter_note_at_frame (const framepos_t frame) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       const double minute =  minute_at_frame (frame);
 
 
-       const double ret = quarter_note_at_frame_locked (_metrics, frame);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       return ret;
+       return pulse_at_minute_locked (_metrics, minute) * 4.0;
 }
 
 double
 }
 
 double
-TempoMap::quarter_note_at_frame_locked (const Metrics& metrics, const framepos_t frame) const
+TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
 {
 {
-       const double ret = pulse_at_frame_locked (metrics, frame) * 4.0;
-
-       return ret;
-}
+       const double minute =  minute_at_frame (frame);
 
 
-double
-TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
-{
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
        }
 
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
        }
 
-       const double ret = pulse_at_frame_locked (_metrics, frame) * 4.0;
-
-       return ret;
+       return pulse_at_minute_locked (_metrics, minute) * 4.0;
 }
 
 }
 
+/**
+ * Returns the frame position corresponding to the supplied quarter-note beat.
+ *
+ * @param quarter_note the quarter-note position.
+ * @return the frame position of the supplied quarter-note. Ignores meter.
+ *
+ *
+*/
 framepos_t
 framepos_t
-TempoMap::frame_at_quarter_note (const double quarter_note)
+TempoMap::frame_at_quarter_note (const double quarter_note) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       double minute;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       const framepos_t ret = frame_at_quarter_note_locked (_metrics, quarter_note);
+               minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
+       }
 
 
-       return ret;
+       return frame_at_minute (minute);
 }
 
 }
 
-framepos_t
-TempoMap::frame_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
+/** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The quarter-note position of the supplied BBT (meter-based) beat.
+ *
+ * a quarter-note may be compared with and assigned to Evoral::Beats.
+ *
+ */
+double
+TempoMap::quarter_note_at_beat (const double beat) const
 {
 {
-       const framepos_t ret = frame_at_pulse_locked (metrics, quarter_note / 4.0);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       return ret;
+       return pulse_at_beat_locked (_metrics, beat) * 4.0;
 }
 
 }
 
+/** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
+ * @param quarter_note The position in quarter-note beats.
+ * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
+ *
+ * a quarter-note is the musical unit of Evoral::Beats.
+ *
+ */
 double
 double
-TempoMap::quarter_note_at_beat (const double beat)
+TempoMap::beat_at_quarter_note (const double quarter_note) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       const double ret = quarter_note_at_beat_locked (_metrics, beat);
+       return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
+}
 
 
-       return ret;
+/** Returns the duration in frames between two supplied quarter-note beat positions.
+ * @param start the first position in quarter-note beats.
+ * @param end the end position in quarter-note beats.
+ * @return the frame distance ober the quarter-note beats duration.
+ *
+ * use this rather than e.g.
+ * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
+ * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
+ *
+ */
+framecnt_t
+TempoMap::frames_between_quarter_notes (const double start, const double end) const
+{
+       double minutes;
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
+       }
+
+       return frame_at_minute (minutes);
 }
 
 double
 }
 
 double
-TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
+TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
 {
 {
-       const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
 
 
-       return ret;
+       return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
 }
 
 double
 }
 
 double
-TempoMap::beat_at_quarter_note (const double quarter_note)
+TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       const double ret = beat_at_pulse_locked (_metrics, quarter_note / 4.0);
+       return quarter_notes_between_frames_locked (_metrics, start, end);
+}
 
 
-       return ret;
+double
+TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
+{
+       const TempoSection* prev_t = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (prev_t && t->frame() > start) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+       }
+       assert (prev_t);
+       const double start_qn = prev_t->pulse_at_frame (start);
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (prev_t && t->frame() > end) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+       }
+       const double end_qn = prev_t->pulse_at_frame (end);
+
+       return (end_qn - start_qn) * 4.0;
 }
 
 bool
 }
 
 bool
@@ -2198,12 +2621,12 @@ TempoMap::check_solved (const Metrics& metrics) const
                        }
                        if (prev_t) {
                                /* check ordering */
                        }
                        if (prev_t) {
                                /* check ordering */
-                               if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
+                               if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
                                        return false;
                                }
 
                                /* precision check ensures tempo and frames align.*/
                                        return false;
                                }
 
                                /* precision check ensures tempo and frames align.*/
-                               if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
+                               if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
                                        if (!t->locked_to_meter()) {
                                                return false;
                                        }
                                        if (!t->locked_to_meter()) {
                                                return false;
                                        }
@@ -2212,8 +2635,8 @@ TempoMap::check_solved (const Metrics& metrics) const
                                /* gradient limit - who knows what it should be?
                                   things are also ok (if a little chaotic) without this
                                */
                                /* gradient limit - who knows what it should be?
                                   things are also ok (if a little chaotic) without this
                                */
-                               if (fabs (prev_t->c_func()) > 1000.0) {
-                                       //std::cout << "c : " << prev_t->c_func() << std::endl;
+                               if (fabs (prev_t->c()) > 1000.0) {
+                                       //std::cout << "c : " << prev_t->c() << std::endl;
                                        return false;
                                }
                        }
                                        return false;
                                }
                        }
@@ -2223,18 +2646,11 @@ TempoMap::check_solved (const Metrics& metrics) const
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (prev_m && m->position_lock_style() == AudioTime) {
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (prev_m && m->position_lock_style() == AudioTime) {
-                               const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
-                               const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
+                               const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
+                               const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
                                /* Here we check that a preceding section of music doesn't overlap a subsequent one.
                                /* Here we check that a preceding section of music doesn't overlap a subsequent one.
-                                  It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
-                                  (they place an exact pulse at a particular time expressed only in frames).
-                                  This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
-                                  away from its actual frame (which is now the frame location of the exact pulse).
-                                  This can result in the calculated frame (from the previous musical section)
-                                  differing from the exact frame by one sample.
-                                  Allow for that.
                                */
                                */
-                               if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
+                               if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
                                        return false;
                                }
                        }
                                        return false;
                                }
                        }
@@ -2248,23 +2664,23 @@ TempoMap::check_solved (const Metrics& metrics) const
 }
 
 bool
 }
 
 bool
-TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
+TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
 {
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
 {
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
-                       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) {
+                       if (t->locked_to_meter()) {
                                t->set_active (true);
                                t->set_active (true);
-                       } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
-                               return false;
+                       } else if (t->position_lock_style() == AudioTime) {
+                               if (t->frame() < frame) {
+                                       t->set_active (false);
+                                       t->set_pulse (-1.0);
+                               } else if (t->frame() > frame) {
+                                       t->set_active (true);
+                               } else if (t->frame() == frame) {
+                                       return false;
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -2272,29 +2688,30 @@ TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
 }
 
 bool
 }
 
 bool
-TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
+TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
 {
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
 {
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
-       framepos_t first_m_frame = 0;
+       double first_m_minute = 0.0;
+       const bool sml = section->locked_to_meter();
 
        /* can't move a tempo before the first meter */
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
 
        /* can't move a tempo before the first meter */
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
-                       if (!m->movable()) {
-                               first_m_frame = m->frame();
+                       if (m->initial()) {
+                               first_m_minute = m->minute();
                                break;
                        }
                }
        }
                                break;
                        }
                }
        }
-       if (section->movable() && frame <= first_m_frame) {
+       if (!section->initial() && minute <= first_m_minute) {
                return false;
        }
 
        section->set_active (true);
                return false;
        }
 
        section->set_active (true);
-       section->set_frame (frame);
+       section->set_minute (minute);
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                TempoSection* t;
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                TempoSection* t;
@@ -2304,21 +2721,36 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
+
                        if (prev_t) {
                        if (prev_t) {
+
                                if (t == section) {
                                if (t == section) {
+                                       continue;
+                               }
+
+                               if (t->frame() == frame_at_minute (minute)) {
+                                       return false;
+                               }
+
+                               const bool tlm = t->position_lock_style() == MusicTime;
+
+                               if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
                                        section_prev = prev_t;
                                        section_prev = prev_t;
-                                       if (t->locked_to_meter()) {
-                                               prev_t = t;
+
+                                       section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
+                                       if (!section->locked_to_meter()) {
+                                               section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
                                        }
                                        }
-                                       continue;
+                                       prev_t = section;
                                }
                                }
+
                                if (t->position_lock_style() == MusicTime) {
                                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));
+                                       prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
+                                       t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
                                } else {
                                } else {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -2326,13 +2758,6 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                }
        }
 
                }
        }
 
-       if (section_prev) {
-               section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
-               if (!section->locked_to_meter()) {
-                       section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
-               }
-       }
-
 #if (0)
        recompute_tempi (imaginary);
 
 #if (0)
        recompute_tempi (imaginary);
 
@@ -2370,7 +2795,7 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                t->set_pulse (0.0);
                                prev_t = t;
                                continue;
                                t->set_pulse (0.0);
                                prev_t = t;
                                continue;
@@ -2380,13 +2805,14 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
                                        section_prev = prev_t;
                                        continue;
                                }
                                        section_prev = prev_t;
                                        continue;
                                }
+
                                if (t->position_lock_style() == MusicTime) {
                                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));
+                                       prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
+                                       t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
                                } else {
                                } else {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -2395,8 +2821,8 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
        }
 
        if (section_prev) {
        }
 
        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));
+               section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
+               section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
        }
 
 #if (0)
        }
 
 #if (0)
@@ -2431,17 +2857,17 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
 }
 
 bool
 }
 
 bool
-TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
+TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
 {
 {
-       /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
-       const MeterSection* other =  &meter_section_at_frame_locked (imaginary, frame);
-       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
+       /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
+       const MeterSection* other =  &meter_section_at_minute_locked (imaginary, minute);
+       if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
                return false;
        }
 
                return false;
        }
 
-       if (!section->movable()) {
+       if (section->initial()) {
                /* lock the first tempo to our first meter */
                /* lock the first tempo to our first meter */
-               if (!set_active_tempos (imaginary, frame)) {
+               if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
                        return false;
                }
        }
                        return false;
                }
        }
@@ -2452,7 +2878,7 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                TempoSection* t;
                if ((*ii)->is_tempo()) {
                        t = static_cast<TempoSection*> (*ii);
                TempoSection* t;
                if ((*ii)->is_tempo()) {
                        t = static_cast<TempoSection*> (*ii);
-                       if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
+                       if (t->locked_to_meter() && t->frame() == section->frame()) {
                                meter_locked_tempo = t;
                                break;
                        }
                                meter_locked_tempo = t;
                                break;
                        }
@@ -2473,22 +2899,25 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (m == section){
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (m == section){
-                               if (prev_m && section->movable()) {
-                                       const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
+                               if (prev_m && !section->initial()) {
+                                       const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
                                        if (beats + prev_m->beat() < section->beat()) {
                                        if (beats + prev_m->beat() < section->beat()) {
-                                               /* set the frame/pulse corresponding to its musical position,
+                                               /* set the section pulse according to its musical position,
                                                 * as an earlier time than this has been requested.
                                                */
                                                const double new_pulse = ((section->beat() - prev_m->beat())
                                                                          / prev_m->note_divisor()) + prev_m->pulse();
 
                                                 * as an earlier time than this has been requested.
                                                */
                                                const double new_pulse = ((section->beat() - prev_m->beat())
                                                                          / prev_m->note_divisor()) + prev_m->pulse();
 
-                                               const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
-
-                                               if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
-                                                       meter_locked_tempo->set_pulse (new_pulse);
-                                                       solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
-                                                       section->set_frame (smallest_frame);
+                                               tempo_copy->set_position_lock_style (MusicTime);
+                                               if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
+                                                       meter_locked_tempo->set_position_lock_style (MusicTime);
+                                                       section->set_position_lock_style (MusicTime);
                                                        section->set_pulse (new_pulse);
                                                        section->set_pulse (new_pulse);
+                                                       solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
+                                                       meter_locked_tempo->set_position_lock_style (AudioTime);
+                                                       section->set_position_lock_style (AudioTime);
+                                                       section->set_minute (meter_locked_tempo->minute());
+
                                                } else {
                                                        solved = false;
                                                }
                                                } else {
                                                        solved = false;
                                                }
@@ -2504,17 +2933,19 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                                                }
                                        } else {
                                                /* all is ok. set section's locked tempo if allowed.
                                                }
                                        } else {
                                                /* all is ok. set section's locked tempo if allowed.
-                                                  possibly disallowed if there is an adjacent audio-locked tempo.
+                                                  possibly disallow if there is an adjacent audio-locked tempo.
                                                   XX this check could possibly go. its never actually happened here.
                                                */
                                                   XX this check could possibly go. its never actually happened here.
                                                */
-                                               MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
-                                               meter_copy->set_frame (frame);
+                                               MeterSection* meter_copy = const_cast<MeterSection*>
+                                                       (&meter_section_at_minute_locked (future_map, section->minute()));
+
+                                               meter_copy->set_minute (minute);
 
 
-                                               if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
-                                                       section->set_frame (frame);
+                                               if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
+                                                       section->set_minute (minute);
                                                        meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
                                                                                                / prev_m->note_divisor()) + prev_m->pulse());
                                                        meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
                                                                                                / prev_m->note_divisor()) + prev_m->pulse());
-                                                       solve_map_frame (imaginary, meter_locked_tempo, frame);
+                                                       solve_map_minute (imaginary, meter_locked_tempo, minute);
                                                } else {
                                                        solved = false;
                                                }
                                                } else {
                                                        solved = false;
                                                }
@@ -2530,16 +2961,16 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                                                }
                                        }
                                } else {
                                                }
                                        }
                                } else {
-                                       /* not movable (first meter atm) */
+                                       /* initial (first meter atm) */
 
 
-                                       tempo_copy->set_frame (frame);
+                                       tempo_copy->set_minute (minute);
                                        tempo_copy->set_pulse (0.0);
 
                                        tempo_copy->set_pulse (0.0);
 
-                                       if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
-                                               section->set_frame (frame);
-                                               meter_locked_tempo->set_frame (frame);
+                                       if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
+                                               section->set_minute (minute);
+                                               meter_locked_tempo->set_minute (minute);
                                                meter_locked_tempo->set_pulse (0.0);
                                                meter_locked_tempo->set_pulse (0.0);
-                                               solve_map_frame (imaginary, meter_locked_tempo, frame);
+                                               solve_map_minute (imaginary, meter_locked_tempo, minute);
                                        } else {
                                                solved = false;
                                        }
                                        } else {
                                                solved = false;
                                        }
@@ -2595,20 +3026,25 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                MeterSection* m;
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                MeterSection* m;
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
+
+                       if (m == section) {
+                               continue;
+                       }
+
                        pair<double, BBT_Time> b_bbt;
                        double new_pulse = 0.0;
 
                        if (prev_m && m->bbt().bars > when.bars && !section_prev){
                                section_prev = prev_m;
                        pair<double, BBT_Time> b_bbt;
                        double new_pulse = 0.0;
 
                        if (prev_m && m->bbt().bars > when.bars && !section_prev){
                                section_prev = prev_m;
+
                                const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
                                const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
                                pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
 
                                section->set_beat (b_bbt);
                                section->set_pulse (pulse);
                                const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
                                const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
                                pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
 
                                section->set_beat (b_bbt);
                                section->set_pulse (pulse);
-                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                               section->set_minute (minute_at_pulse_locked (imaginary, pulse));
                                prev_m = section;
                                prev_m = section;
-                               continue;
                        }
 
                        if (m->position_lock_style() == AudioTime) {
                        }
 
                        if (m->position_lock_style() == AudioTime) {
@@ -2618,7 +3054,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                                        TempoSection* t;
                                        if ((*ii)->is_tempo()) {
                                                t = static_cast<TempoSection*> (*ii);
                                        TempoSection* t;
                                        if ((*ii)->is_tempo()) {
                                                t = static_cast<TempoSection*> (*ii);
-                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
+                                               if (t->locked_to_meter() && t->frame() == m->frame()) {
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
@@ -2630,14 +3066,21 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                                }
 
                                if (prev_m) {
                                }
 
                                if (prev_m) {
-                                       const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
+                                       double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
 
                                        if (beats + prev_m->beat() != m->beat()) {
                                                /* tempo/ meter change caused a change in beat (bar). */
 
                                        if (beats + prev_m->beat() != m->beat()) {
                                                /* tempo/ meter change caused a change in beat (bar). */
+
+                                               /* the user has requested that the previous section of music overlaps this one.
+                                                  we have no choice but to change the bar number here, as being locked to audio means
+                                                  we must stay where we are on the timeline.
+                                               */
+                                               beats = m->beat() - prev_m->beat();
                                                b_bbt = make_pair (beats + prev_m->beat()
                                                                   , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
                                                new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                                b_bbt = make_pair (beats + prev_m->beat()
                                                                   , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
                                                new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
-                                       } else if (m->movable()) {
+
+                                       } else if (!m->initial()) {
                                                b_bbt = make_pair (m->beat(), m->bbt());
                                                new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                        }
                                                b_bbt = make_pair (m->beat(), m->bbt());
                                                new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                        }
@@ -2663,7 +3106,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                                new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                m->set_beat (b_bbt);
                                m->set_pulse (new_pulse);
                                new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                m->set_beat (b_bbt);
                                m->set_pulse (new_pulse);
-                               m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
+                               m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
                        }
 
                        prev_m = m;
                        }
 
                        prev_m = m;
@@ -2678,7 +3121,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
 
                section->set_beat (b_bbt);
                section->set_pulse (pulse);
 
                section->set_beat (b_bbt);
                section->set_pulse (pulse);
-               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+               section->set_minute (minute_at_pulse_locked (imaginary, pulse));
        }
 
        MetricSectionSorter cmp;
        }
 
        MetricSectionSorter cmp;
@@ -2753,7 +3196,7 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSe
 /** answers the question "is this a valid beat position for this tempo section?".
  *  it returns true if the tempo section can be moved to the requested bbt position,
  *  leaving the tempo map in a solved state.
 /** answers the question "is this a valid beat position for this tempo section?".
  *  it returns true if the tempo section can be moved to the requested bbt position,
  *  leaving the tempo map in a solved state.
- * @param section the tempo section to be moved
+ * @param ts the tempo section to be moved
  * @param bbt the requested new position for the tempo section
  * @return true if the tempo section can be moved to the position, otherwise false.
  */
  * @param bbt the requested new position for the tempo section
  * @return true if the tempo section can be moved to the position, otherwise false.
  */
@@ -2786,7 +3229,7 @@ TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
 * taking any possible reordering as a consequence of this into account.
 * @param section - the section to be altered
 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
 * taking any possible reordering as a consequence of this into account.
 * @param section - the section to be altered
-* @param bbt - the bbt where the altered tempo will fall
+* @param bbt - the BBT time  where the altered tempo will fall
 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
 */
 pair<double, framepos_t>
 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
 */
 pair<double, framepos_t>
@@ -2801,6 +3244,10 @@ TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 
        const double beat = beat_at_bbt_locked (future_map, bbt);
 
 
        const double beat = beat_at_bbt_locked (future_map, bbt);
 
+       if (section->position_lock_style() == AudioTime) {
+               tempo_copy->set_position_lock_style (MusicTime);
+       }
+
        if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
                ret.first = tempo_copy->pulse();
                ret.second = tempo_copy->frame();
        if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
                ret.first = tempo_copy->pulse();
                ret.second = tempo_copy->frame();
@@ -2817,8 +3264,24 @@ TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
        return ret;
 }
 
        return ret;
 }
 
+/** moves a TempoSection to a specified position.
+ * @param ts - the section to be moved
+ * @param frame - the new position in frames for the tempo
+ * @param sub_num - the snap division to use if using musical time.
+ *
+ * if sub_num is non-zero, the frame position is used to calculate an exact
+ * musical position.
+ * sub_num   | effect
+ * -1        | snap to bars (meter-based)
+ *  0        | no snap - use audio frame for musical position
+ *  1        | snap to meter-based (BBT) beat
+ * >1        | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
+ *
+ * this follows the snap convention in the gui.
+ * if sub_num is zero, the musical position will be taken from the supplied frame.
+ */
 void
 void
-TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
+TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
 {
        Metrics future_map;
 
 {
        Metrics future_map;
 
@@ -2830,7 +3293,7 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
 
                        tempo_copy->set_position_lock_style (AudioTime);
 
 
                        tempo_copy->set_position_lock_style (AudioTime);
 
-                       if (solve_map_frame (future_map, tempo_copy, frame)) {
+                       if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
                                const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
                                const double pulse = pulse_at_beat_locked (future_map, beat);
 
                                const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
                                const double pulse = pulse_at_beat_locked (future_map, beat);
 
@@ -2847,30 +3310,26 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
 
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
 
-                       if (solve_map_frame (future_map, tempo_copy, frame)) {
+                       if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
                                if (sub_num != 0) {
                                        /* We're moving the object that defines the grid while snapping to it...
                                         * Placing the ts at the beat corresponding to the requested frame may shift the
                                         * grid in such a way that the mouse is left hovering over a completerly different division,
                                         * causing jittering when the mouse next moves (esp. large tempo deltas).
                                if (sub_num != 0) {
                                        /* We're moving the object that defines the grid while snapping to it...
                                         * Placing the ts at the beat corresponding to the requested frame may shift the
                                         * grid in such a way that the mouse is left hovering over a completerly different division,
                                         * causing jittering when the mouse next moves (esp. large tempo deltas).
-                                        * To avoid this, place the ts at the requested frame in a dummy map
-                                        * then find the closest beat subdivision to that frame in the dummy.
+                                        *
                                         * This alters the snap behaviour slightly in that we snap to beat divisions
                                         * in the future map rather than the existing one.
                                         */
                                         * This alters the snap behaviour slightly in that we snap to beat divisions
                                         * in the future map rather than the existing one.
                                         */
-                                       const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
-                                       const double pulse = pulse_at_beat_locked (future_map, beat);
-
-                                       if (solve_map_pulse (future_map, tempo_copy, pulse)) {
-                                               /* snapping to a grid. force MusicTime temporarily. */
-                                               ts->set_position_lock_style (MusicTime);
-                                               solve_map_pulse (_metrics, ts, pulse);
-                                               ts->set_position_lock_style (AudioTime);
+                                       const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
+                                       const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
 
 
+                                       if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
+                                               solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
+                                               ts->set_pulse (qn / 4.0);
                                                recompute_meters (_metrics);
                                        }
                                } else {
                                                recompute_meters (_metrics);
                                        }
                                } else {
-                                       solve_map_frame (_metrics, ts, frame);
+                                       solve_map_minute (_metrics, ts, minute_at_frame (frame));
                                        recompute_meters (_metrics);
                                }
                        }
                                        recompute_meters (_metrics);
                                }
                        }
@@ -2883,11 +3342,19 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
                ++d;
        }
 
                ++d;
        }
 
-       MetricPositionChanged (); // Emit Signal
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
 }
 
 }
 
+/** moves a MeterSection to a specified position.
+ * @param ms - the section to be moved
+ * @param frame - the new position in frames for the meter
+ *
+ * as a meter cannot snap to anything but bars,
+ * the supplied frame is rounded to the nearest bar, possibly
+ * leaving the meter position unchanged.
+ */
 void
 void
-TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
+TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
 {
        Metrics future_map;
 
 {
        Metrics future_map;
 
@@ -2897,8 +3364,8 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
 
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
 
-                       if (solve_map_frame (future_map, copy, frame)) {
-                               solve_map_frame (_metrics, ms, frame);
+                       if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
+                               solve_map_minute (_metrics, ms, minute_at_frame (frame));
                                recompute_tempi (_metrics);
                        }
                }
                                recompute_tempi (_metrics);
                        }
                }
@@ -2907,7 +3374,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
 
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
 
-                       const double beat = beat_at_frame_locked (_metrics, frame);
+                       const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
                        const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
 
                        if (solve_map_bbt (future_map, copy, bbt)) {
                        const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
 
                        if (solve_map_bbt (future_map, copy, bbt)) {
@@ -2923,7 +3390,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
                ++d;
        }
 
                ++d;
        }
 
-       MetricPositionChanged (); // Emit Signal
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
 }
 
 bool
 }
 
 bool
@@ -2934,11 +3401,40 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
-               tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
+
+               if (tempo_copy->type() == TempoSection::Constant) {
+                       tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
+                       tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
+               } else {
+                       tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
+                       tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
+               }
+
+               if (ts->clamped()) {
+                       TempoSection* prev = 0;
+                       if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
+                               prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
+                       }
+               }
+
                recompute_tempi (future_map);
 
                if (check_solved (future_map)) {
                recompute_tempi (future_map);
 
                if (check_solved (future_map)) {
-                       ts->set_beats_per_minute (bpm.beats_per_minute());
+                       if (ts->type() == TempoSection::Constant) {
+                               ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
+                               ts->set_note_types_per_minute (bpm.note_types_per_minute());
+                       } else {
+                               ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
+                               ts->set_note_types_per_minute (bpm.note_types_per_minute());
+                       }
+
+                       if (ts->clamped()) {
+                               TempoSection* prev = 0;
+                               if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
+                                       prev->set_end_note_types_per_minute (ts->note_types_per_minute());
+                               }
+                       }
+
                        recompute_map (_metrics);
                        can_solve = true;
                }
                        recompute_map (_metrics);
                        can_solve = true;
                }
@@ -2950,20 +3446,21 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
                ++d;
        }
        if (can_solve) {
                ++d;
        }
        if (can_solve) {
-               MetricPositionChanged (); // Emit Signal
+               MetricPositionChanged (PropertyChange ()); // Emit Signal
        }
        }
+
        return can_solve;
 }
 
 void
        return can_solve;
 }
 
 void
-TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
+TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
 {
        /*
          Ts (future prev_t)   Tnext
          |                    |
          |     [drag^]        |
          |----------|----------
 {
        /*
          Ts (future prev_t)   Tnext
          |                    |
          |     [drag^]        |
          |----------|----------
-               e_f  pulse(frame)
+               e_f  qn_beats(frame)
        */
 
        Metrics future_map;
        */
 
        Metrics future_map;
@@ -2976,149 +3473,399 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                }
 
                TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
                }
 
                TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
-               TempoSection* prev_to_prev_t = 0;
-               const frameoffset_t fr_off = end_frame - frame;
 
 
-               if (prev_t && prev_t->pulse() > 0.0) {
-                       prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
+               if (!prev_t) {
+                       return;
                }
 
                }
 
-               TempoSection* next_t = 0;
-               for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
-                       TempoSection* t = 0;
-                       if ((*i)->is_tempo()) {
-                               t = static_cast<TempoSection*> (*i);
-                               if (t->frame() > ts->frame()) {
-                                       next_t = t;
-                                       break;
-                               }
-                       }
-               }
                /* minimum allowed measurement distance in frames */
                /* minimum allowed measurement distance in frames */
-               const framepos_t min_dframe = 2;
-
-               /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
-                  constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
-               */
-               double contribution = 0.0;
-
-               if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
-                       contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
-               }
-
-               const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
-
-               const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
-               const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+               framepos_t const min_dframe = 2;
 
                double new_bpm;
 
                double new_bpm;
+               if (prev_t->clamped()) {
+                       TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
+                       TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
+                       /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
+                          constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
+                       */
+                       double contribution = 0.0;
+                       if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+                               contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
+                       }
+                       framepos_t const fr_off = (end_frame - frame);
+                       const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
 
 
-               if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
-
-                       if (prev_t->position_lock_style() == MusicTime) {
-                               if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
-                                       if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
-
-                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
-                                                                                       / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
-                                       } else {
-                                               new_bpm = prev_t->beats_per_minute();
-                                       }
-                               } else {
-                                       /* prev to prev is irrelevant */
-
-                                       if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
-                                               new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
-                                       } else {
-                                               new_bpm = prev_t->beats_per_minute();
-                                       }
-                               }
+                       if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
+                               new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
+                                                                                    / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
                        } else {
                        } else {
-                               /* AudioTime */
-                               if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
-                                       if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
-
-                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
-                                                                                       / (double) ((end_frame) - prev_to_prev_t->frame()));
-                                       } else {
-                                               new_bpm = prev_t->beats_per_minute();
-                                       }
-                               } else {
-                                       /* prev_to_prev_t is irrelevant */
-
-                                       if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
-                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
-                                       } else {
-                                               new_bpm = prev_t->beats_per_minute();
-                                       }
-                               }
+                               new_bpm = prev_t->note_types_per_minute();
                        }
                } else {
                        }
                } else {
+                       if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
 
 
-                       double frame_ratio = 1.0;
-                       double pulse_ratio = 1.0;
-                       const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
-
-                       if (prev_to_prev_t) {
-                               if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
-                                       frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
-                               }
-                               if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
-                                       pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
-                               }
+                               new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
+                                                                            / (double) (end_frame - prev_t->frame()));
                        } else {
                        } else {
-                               if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
-                                       frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
-                               }
-                               pulse_ratio = (start_pulse / end_pulse);
+                               new_bpm = prev_t->note_types_per_minute();
                        }
                        }
-                       new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
-               }
 
 
+                       new_bpm = min (new_bpm, (double) 1000.0);
+               }
                /* don't clamp and proceed here.
                   testing has revealed that this can go negative,
                   which is an entirely different thing to just being too low.
                */
                /* don't clamp and proceed here.
                   testing has revealed that this can go negative,
                   which is an entirely different thing to just being too low.
                */
+
                if (new_bpm < 0.5) {
                if (new_bpm < 0.5) {
-                       return;
+                       goto out;
                }
                }
-               new_bpm = min (new_bpm, (double) 1000.0);
-               prev_t->set_beats_per_minute (new_bpm);
+
+               if (prev_t && prev_t->type() == TempoSection::Ramp) {
+                       prev_t->set_note_types_per_minute (new_bpm);
+               } else {
+                       prev_t->set_end_note_types_per_minute (new_bpm);
+                       prev_t->set_note_types_per_minute (new_bpm);
+               }
+
+               if (prev_t->clamped()) {
+                       TempoSection* prev = 0;
+                       if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
+                               prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
+                       }
+               }
+
                recompute_tempi (future_map);
                recompute_meters (future_map);
 
                if (check_solved (future_map)) {
                recompute_tempi (future_map);
                recompute_meters (future_map);
 
                if (check_solved (future_map)) {
-                       ts->set_beats_per_minute (new_bpm);
+                       if (prev_t && prev_t->type() == TempoSection::Ramp) {
+                               ts->set_note_types_per_minute (new_bpm);
+                       } else {
+                               ts->set_end_note_types_per_minute (new_bpm);
+                               ts->set_note_types_per_minute (new_bpm);
+                       }
+                       if (ts->clamped()) {
+                               TempoSection* prev = 0;
+                               if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
+                                       prev->set_end_note_types_per_minute (ts->note_types_per_minute());
+                               }
+                       }
                        recompute_tempi (_metrics);
                        recompute_meters (_metrics);
                }
        }
 
                        recompute_tempi (_metrics);
                        recompute_meters (_metrics);
                }
        }
 
+
+out:
        Metrics::const_iterator d = future_map.begin();
        while (d != future_map.end()) {
                delete (*d);
                ++d;
        }
        Metrics::const_iterator d = future_map.begin();
        while (d != future_map.end()) {
                delete (*d);
                ++d;
        }
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
+
 
 
-       MetricPositionChanged (); // Emit Signal
 }
 }
+void
+TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
+{
+       /*
+         Ts (future prev_t)   Tnext
+         |                    |
+         |     [drag^]        |
+         |----------|----------
+               e_f  qn_beats(frame)
+       */
 
 
-/** Returns the exact beat corresponding to the bar, beat or quarter note subdivision nearest to
- * the supplied frame, possibly returning a negative value.
+       Metrics future_map;
+
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+
+               if (!ts) {
+                       return;
+               }
+
+               TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
+
+               if (!prev_t) {
+                       return;
+               }
+
+               /* minimum allowed measurement distance in frames */
+               framepos_t const min_dframe = 2;
+               double new_bpm;
+
+               if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
+                       new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
+                                                                                / (double) (prev_t->frame() - end_frame));
+               } else {
+                       new_bpm = prev_t->end_note_types_per_minute();
+               }
+
+               new_bpm = min (new_bpm, (double) 1000.0);
+
+               if (new_bpm < 0.5) {
+                       goto out;
+               }
+
+               prev_t->set_end_note_types_per_minute (new_bpm);
+
+               TempoSection* next = 0;
+               if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
+                       if (next->clamped()) {
+                               next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
+                       }
+               }
+
+               recompute_tempi (future_map);
+               recompute_meters (future_map);
+
+               if (check_solved (future_map)) {
+                       ts->set_end_note_types_per_minute (new_bpm);
+
+                       TempoSection* true_next = 0;
+                       if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
+                               if (true_next->clamped()) {
+                                       true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
+                               }
+                       }
+
+                       recompute_tempi (_metrics);
+                       recompute_meters (_metrics);
+               }
+       }
+
+
+out:
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
+}
+
+bool
+TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
+{
+       TempoSection* next_t = 0;
+       TempoSection* next_to_next_t = 0;
+       Metrics future_map;
+       bool can_solve = false;
+
+       /* minimum allowed measurement distance in frames */
+       framepos_t const min_dframe = 2;
+
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               if (!ts) {
+                       return false;
+               }
+
+               TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+               TempoSection* prev_to_prev_t = 0;
+               const frameoffset_t fr_off = end_frame - frame;
+
+               if (!tempo_copy) {
+                       return false;
+               }
+
+               if (tempo_copy->pulse() > 0.0) {
+                       prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
+               }
+
+               for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
+                       if ((*i)->is_tempo() && (*i)->minute() >  tempo_copy->minute()) {
+                               next_t = static_cast<TempoSection*> (*i);
+                               break;
+                       }
+               }
+
+               if (!next_t) {
+                       return false;
+               }
+
+               for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
+                       if ((*i)->is_tempo() && (*i)->minute() >  next_t->minute()) {
+                               next_to_next_t = static_cast<TempoSection*> (*i);
+                               break;
+                       }
+               }
+
+               if (!next_to_next_t) {
+                       return false;
+               }
+
+               double prev_contribution = 0.0;
+
+               if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+                       prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
+               }
+
+               const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
+
+
+               framepos_t old_tc_minute = tempo_copy->minute();
+               double old_next_minute = next_t->minute();
+               double old_next_to_next_minute = next_to_next_t->minute();
+
+               double new_bpm;
+               double new_next_bpm;
+               double new_copy_end_bpm;
+
+               if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
+                       new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
+                                                                                      / (double) (end_frame - tempo_copy->frame()));
+               } else {
+                       new_bpm = tempo_copy->note_types_per_minute();
+               }
+
+               /* don't clamp and proceed here.
+                  testing has revealed that this can go negative,
+                  which is an entirely different thing to just being too low.
+               */
+               if (new_bpm < 0.5) {
+                       return false;
+               }
+
+               new_bpm = min (new_bpm, (double) 1000.0);
+
+               tempo_copy->set_note_types_per_minute (new_bpm);
+               if (tempo_copy->type() == TempoSection::Constant) {
+                       tempo_copy->set_end_note_types_per_minute (new_bpm);
+               }
+
+               recompute_tempi (future_map);
+
+               if (check_solved (future_map)) {
+
+                       if (!next_t) {
+                               return false;
+                       }
+
+                       ts->set_note_types_per_minute (new_bpm);
+                       if (ts->type() == TempoSection::Constant) {
+                               ts->set_end_note_types_per_minute (new_bpm);
+                       }
+
+                       recompute_map (_metrics);
+
+                       can_solve = true;
+               }
+
+               if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
+                       if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
+
+                               new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
+                                                                                 / (double) ((old_next_to_next_minute) - old_next_minute));
+
+                       } else {
+                               new_next_bpm = next_t->note_types_per_minute();
+                       }
+
+                       next_t->set_note_types_per_minute (new_next_bpm);
+                       recompute_tempi (future_map);
+
+                       if (check_solved (future_map)) {
+                               for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+                                       if ((*i)->is_tempo() && (*i)->minute() >  ts->minute()) {
+                                               next_t = static_cast<TempoSection*> (*i);
+                                               break;
+                                       }
+                               }
+
+                               if (!next_t) {
+                                       return false;
+                               }
+                               next_t->set_note_types_per_minute (new_next_bpm);
+                               recompute_map (_metrics);
+                               can_solve = true;
+                       }
+               } else {
+                       double next_frame_ratio = 1.0;
+                       double copy_frame_ratio = 1.0;
+
+                       if (next_to_next_t) {
+                               next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute -  old_next_minute);
+
+                               copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
+                       }
+
+                       new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
+                       new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
+
+                       tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
+
+                       if (next_t->clamped()) {
+                               next_t->set_note_types_per_minute (new_copy_end_bpm);
+                       } else {
+                               next_t->set_note_types_per_minute (new_next_bpm);
+                       }
+
+                       recompute_tempi (future_map);
+
+                       if (check_solved (future_map)) {
+                               for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+                                       if ((*i)->is_tempo() && (*i)->minute() >  ts->minute()) {
+                                               next_t = static_cast<TempoSection*> (*i);
+                                               break;
+                                       }
+                               }
+
+                               if (!next_t) {
+                                       return false;
+                               }
+
+                               if (next_t->clamped()) {
+                                       next_t->set_note_types_per_minute (new_copy_end_bpm);
+                               } else {
+                                       next_t->set_note_types_per_minute (new_next_bpm);
+                               }
+
+                               ts->set_end_note_types_per_minute (new_copy_end_bpm);
+                               recompute_map (_metrics);
+                               can_solve = true;
+                       }
+               }
+       }
+
+       Metrics::const_iterator d = future_map.begin();
+       while (d != future_map.end()) {
+               delete (*d);
+               ++d;
+       }
+
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
+
+       return can_solve;
+}
+/** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
+ * the supplied frame, possibly returning a negative value.
+ *
  * @param frame  The session frame position.
  * @param sub_num The subdivision to use when rounding the beat.
  * @param frame  The session frame position.
  * @param sub_num The subdivision to use when rounding the beat.
- * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
- * Positive integers indicate quarter note (non BBT) divisions.
- * 0 indicates that the returned beat should not be rounded.
+ *                A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
+ *                Positive integers indicate quarter note (non BBT) divisions.
+ *                0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
  * @return The beat position of the supplied frame.
  * @return The beat position of the supplied frame.
+ *
+ * when working to a musical grid, the use of sub_nom indicates that
+ * the position should be interpreted musically.
+ *
+ * it effectively snaps to meter bars, meter beats or quarter note divisions
+ * (as per current gui convention) and returns a musical position independent of frame rate.
+ *
  * If the supplied frame lies before the first meter, the return will be negative,
  * in which case the returned beat uses the first meter (for BBT subdivisions) and
  * the continuation of the tempo curve (backwards).
  *
  * If the supplied frame lies before the first meter, the return will be negative,
  * in which case the returned beat uses the first meter (for BBT subdivisions) and
  * the continuation of the tempo curve (backwards).
  *
- * This function uses both tempo and meter.
+ * This function is sensitive to tempo and meter.
  */
 double
  */
 double
-TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
+TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
@@ -3126,27 +3873,35 @@ TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
 }
 
 double
 }
 
 double
-TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
+TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
 {
        return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
 }
 
 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
  * the supplied frame, possibly returning a negative value.
 {
        return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
 }
 
 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
  * the supplied frame, possibly returning a negative value.
+ *
  * @param frame  The session frame position.
  * @param sub_num The subdivision to use when rounding the quarter note.
  * @param frame  The session frame position.
  * @param sub_num The subdivision to use when rounding the quarter note.
- * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
- * Positive integers indicate quarter note (non BBT) divisions.
- * 0 indicates that the returned quarter note should not be rounded.
+ *                A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
+ *                Positive integers indicate quarter note (non BBT) divisions.
+ *                0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
  * @return The quarter note position of the supplied frame.
  * @return The quarter note position of the supplied frame.
+ *
+ * When working to a musical grid, the use of sub_nom indicates that
+ * the frame position should be interpreted musically.
+ *
+ * it effectively snaps to meter bars, meter beats or quarter note divisions
+ * (as per current gui convention) and returns a musical position independent of frame rate.
+ *
  * If the supplied frame lies before the first meter, the return will be negative,
  * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
  * the continuation of the tempo curve (backwards).
  *
  * If the supplied frame lies before the first meter, the return will be negative,
  * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
  * the continuation of the tempo curve (backwards).
  *
- * This function uses both tempo and meter.
+ * This function is tempo-sensitive.
  */
 double
  */
 double
-TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
+TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
@@ -3154,24 +3909,24 @@ TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
 }
 
 double
 }
 
 double
-TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
+TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
 {
 {
-       double qn = quarter_note_at_frame_locked (metrics, frame);
+       double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
 
        if (sub_num > 1) {
                qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
        } else if (sub_num == 1) {
                /* the gui requested exact musical (BBT) beat */
 
        if (sub_num > 1) {
                qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
        } else if (sub_num == 1) {
                /* the gui requested exact musical (BBT) beat */
-               qn = quarter_note_at_beat (floor (beat_at_frame_locked (metrics, frame) + 0.5));
+               qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
        } else if (sub_num == -1) {
                /* snap to  bar */
                Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
                bbt.beats = 1;
                bbt.ticks = 0;
 
        } else if (sub_num == -1) {
                /* snap to  bar */
                Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
                bbt.beats = 1;
                bbt.ticks = 0;
 
-               const double prev_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
+               const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
                ++bbt.bars;
                ++bbt.bars;
-               const double next_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
+               const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
 
                if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
                        qn = next_b;
 
                if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
                        qn = next_b;
@@ -3183,131 +3938,98 @@ TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& fr
        return qn;
 }
 
        return qn;
 }
 
+/** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
+ * @param pos the frame position in the tempo map.
+ * @param bbt the distance in BBT time from pos to calculate.
+ * @param dir the rounding direction..
+ * @return the duration in frames between pos and bbt
+*/
 framecnt_t
 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 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 = max (0.0, 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);
-}
+       BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
 
 
-framepos_t
-TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
-{
-       return round_to_type (fr, dir, Bar);
-}
-
-framepos_t
-TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
-{
-       return round_to_type (fr, dir, Beat);
-}
-
-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 (max (0.0, 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;
-
-       ticks -= beats * BBT_Time::ticks_per_beat;
+       const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
 
        if (dir > 0) {
 
        if (dir > 0) {
-               /* round to next (or same iff dir == RoundUpMaybe) */
-
-               uint32_t mod = ticks % ticks_one_subdivisions_worth;
-
-               if (mod == 0 && dir == RoundUpMaybe) {
-                       /* right on the subdivision, which is fine, so do nothing */
+               pos_bbt.bars += bbt.bars;
 
 
-               } else if (mod == 0) {
-                       /* right on the subdivision, so the difference is just the subdivision ticks */
-                       ticks += ticks_one_subdivisions_worth;
-
-               } else {
-                       /* not on subdivision, compute distance to next subdivision */
-
-                       ticks += ticks_one_subdivisions_worth - mod;
+               pos_bbt.ticks += bbt.ticks;
+               if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
+                       pos_bbt.beats += 1;
+                       pos_bbt.ticks -= BBT_Time::ticks_per_beat;
                }
 
                }
 
-               if (ticks >= BBT_Time::ticks_per_beat) {
-                       ticks -= BBT_Time::ticks_per_beat;
+               pos_bbt.beats += bbt.beats;
+               if ((double) pos_bbt.beats > divisions) {
+                       pos_bbt.bars += 1;
+                       pos_bbt.beats -= divisions;
                }
                }
-       } else if (dir < 0) {
+               const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
 
 
-               /* round to previous (or same iff dir == RoundDownMaybe) */
+               return pos_bbt_frame - pos;
 
 
-               uint32_t difference = ticks % ticks_one_subdivisions_worth;
-
-               if (difference == 0 && dir == RoundDownAlways) {
-                       /* right on the subdivision, but force-rounding down,
-                          so the difference is just the subdivision ticks */
-                       difference = ticks_one_subdivisions_worth;
-               }
+       } else {
 
 
-               if (ticks < difference) {
-                       ticks = BBT_Time::ticks_per_beat - ticks;
+               if (pos_bbt.bars <= bbt.bars) {
+                       pos_bbt.bars = 1;
                } else {
                } else {
-                       ticks -= difference;
+                       pos_bbt.bars -= bbt.bars;
                }
 
                }
 
-       } else {
-               /* round to nearest */
-               double rem;
-
-               /* compute the distance to the previous and next subdivision */
-
-               if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
-
-                       /* closer to the next subdivision, so shift forward */
-
-                       ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
-
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
-
-                       if (ticks > BBT_Time::ticks_per_beat) {
-                               ++beats;
-                               ticks -= BBT_Time::ticks_per_beat;
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
+               if (pos_bbt.ticks < bbt.ticks) {
+                       if (pos_bbt.bars > 1) {
+                               if (pos_bbt.beats == 1) {
+                                       pos_bbt.bars--;
+                                       pos_bbt.beats = divisions;
+                               } else {
+                                       pos_bbt.beats--;
+                               }
+                               pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
+                       } else {
+                               pos_bbt.beats = 1;
+                               pos_bbt.ticks = 0;
                        }
                        }
+               } else {
+                       pos_bbt.ticks -= bbt.ticks;
+               }
 
 
-               } else if (rem > 0) {
-
-                       /* closer to previous subdivision, so shift backward */
-
-                       if (rem > ticks) {
-                               if (beats == 0) {
-                                       /* can't go backwards past zero, so ... */
-                                       return 0;
-                               }
-                               /* step back to previous beat */
-                               --beats;
-                               ticks = lrint (BBT_Time::ticks_per_beat - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
+               if (pos_bbt.beats <= bbt.beats) {
+                       if (pos_bbt.bars > 1) {
+                               pos_bbt.bars--;
+                               pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
                        } else {
                        } else {
-                               ticks = lrint (ticks - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
+                               pos_bbt.beats = 1;
                        }
                } else {
                        }
                } else {
-                       /* on the subdivision, do nothing */
+                       pos_bbt.beats -= bbt.beats;
                }
                }
+
+               return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
        }
 
        }
 
-       const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+       return 0;
+}
 
 
-       return ret_frame;
+MusicFrame
+TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
+{
+       return round_to_type (fr, dir, Bar);
 }
 
 }
 
-framepos_t
+MusicFrame
+TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
+{
+       return round_to_type (fr, dir, Beat);
+}
+
+MusicFrame
 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
+       uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
        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 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;
 
@@ -3331,9 +4053,14 @@ TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMo
                        ticks += ticks_one_subdivisions_worth - mod;
                }
 
                        ticks += ticks_one_subdivisions_worth - mod;
                }
 
-               if (ticks >= BBT_Time::ticks_per_beat) {
-                       ticks -= BBT_Time::ticks_per_beat;
-               }
+//NOTE:  this code intentionally limits the rounding so we don't advance to the next beat.
+//  For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
+//     And since the "prev" direction DOES move beats, I assume this code is unintended.
+//  But I'm keeping it around, until we determine there are no terrible consequences.
+//             if (ticks >= BBT_Time::ticks_per_beat) {
+//                     ticks -= BBT_Time::ticks_per_beat;
+//             }
+
        } else if (dir < 0) {
 
                /* round to previous (or same iff dir == RoundDownMaybe) */
        } else if (dir < 0) {
 
                /* round to previous (or same iff dir == RoundDownMaybe) */
@@ -3379,7 +4106,7 @@ TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMo
                        if (rem > ticks) {
                                if (beats == 0) {
                                        /* can't go backwards past zero, so ... */
                        if (rem > ticks) {
                                if (beats == 0) {
                                        /* can't go backwards past zero, so ... */
-                                       return 0;
+                                       return MusicFrame (0, 0);
                                }
                                /* step back to previous beat */
                                --beats;
                                }
                                /* step back to previous beat */
                                --beats;
@@ -3394,88 +4121,133 @@ TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMo
                }
        }
 
                }
        }
 
-       const framepos_t ret_frame = frame_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+       MusicFrame ret (0, 0);
+       ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
+       ret.division = sub_num;
 
 
-       return ret_frame;
+       return ret;
 }
 
 }
 
-framepos_t
+MusicFrame
 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       const double beat_at_framepos = max (0.0, beat_at_frame_locked (_metrics, frame));
+       const double minute = minute_at_frame (frame);
+       const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
        BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
        BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
+       MusicFrame ret (0, 0);
 
        switch (type) {
        case Bar:
 
        switch (type) {
        case Bar:
+               ret.division = -1;
+
                if (dir < 0) {
                        /* find bar previous to 'frame' */
                if (dir < 0) {
                        /* find bar previous to 'frame' */
+                       if (bbt.bars > 0)
+                               --bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_bbt_locked (_metrics, bbt);
+
+                       ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       return ret;
 
                } else if (dir > 0) {
                        /* find bar following 'frame' */
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
 
                } else if (dir > 0) {
                        /* find bar following 'frame' */
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_bbt_locked (_metrics, bbt);
+
+                       ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       return ret;
                } else {
                        /* true rounding: find nearest bar */
                } else {
                        /* true rounding: find nearest bar */
-                       framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
                        bbt.beats = 1;
                        bbt.ticks = 0;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
                        ++bbt.bars;
                        ++bbt.bars;
-                       framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
+                               ret.frame = next_ft;
 
 
-                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
-                               return next_ft;
+                               return ret;
                        } else {
                        } else {
-                               return prev_ft;
+                               --bbt.bars;
+                               ret.frame = prev_ft;
+
+                               return ret;
                        }
                }
 
                break;
 
        case Beat:
                        }
                }
 
                break;
 
        case Beat:
+               ret.division = 1;
+
                if (dir < 0) {
                if (dir < 0) {
-                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
+                       ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
+
+                       return ret;
                } else if (dir > 0) {
                } else if (dir > 0) {
-                       return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
+                       ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
+
+                       return ret;
                } else {
                } else {
-                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
+                       ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
+
+                       return ret;
                }
                break;
        }
 
                }
                break;
        }
 
-       return 0;
+       return MusicFrame (0, 0);
 }
 
 void
 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
 }
 
 void
 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
-                   framepos_t lower, framepos_t upper)
+                   framepos_t lower, framepos_t upper, uint32_t bar_mod)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
+       int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
        framecnt_t pos = 0;
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
 
        framecnt_t pos = 0;
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
 
-       if (frame_at_beat_locked (_metrics, cnt) >= upper) {
+       if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
                return;
        }
                return;
        }
+       if (bar_mod == 0) {
+               while (pos >= 0 && pos < upper) {
+                       pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
+                       const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
 
 
-       while (pos < upper) {
-               pos = frame_at_beat_locked (_metrics, cnt);
-               const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
-               const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
-               const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
-               points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
-               ++cnt;
+                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
+                       ++cnt;
+               }
+       } else {
+               BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
+               bbt.beats = 1;
+               bbt.ticks = 0;
+
+               if (bar_mod != 1) {
+                       bbt.bars -= bbt.bars % bar_mod;
+                       ++bbt.bars;
+               }
+
+               while (pos >= 0 && pos < upper) {
+                       pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+                       const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
+                       bbt.bars += bar_mod;
+               }
        }
 }
 
        }
 }
 
@@ -3483,11 +4255,20 @@ const TempoSection&
 TempoMap::tempo_section_at_frame (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::tempo_section_at_frame (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_section_at_frame_locked (_metrics, frame);
+
+       return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
+}
+
+TempoSection&
+TempoMap::tempo_section_at_frame (framepos_t frame)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 const TempoSection&
 }
 
 const TempoSection&
-TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
+TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
 {
        TempoSection* prev = 0;
 
 {
        TempoSection* prev = 0;
 
@@ -3500,7 +4281,7 @@ TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t fram
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev && t->frame() > frame) {
+                       if (prev && t->minute() > minute) {
                                break;
                        }
 
                                break;
                        }
 
@@ -3515,7 +4296,35 @@ TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t fram
 
        return *prev;
 }
 
        return *prev;
 }
+TempoSection&
+TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
+{
+       TempoSection* prev = 0;
+
+       TempoSection* t;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if (prev && t->minute() > minute) {
+                               break;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
 
 
+       return *prev;
+}
 const TempoSection&
 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
 const TempoSection&
 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
@@ -3527,6 +4336,11 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
                        if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
                                break;
                        }
                        if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
                                break;
                        }
@@ -3537,11 +4351,97 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be
        return *prev_t;
 }
 
        return *prev_t;
 }
 
+TempoSection*
+TempoMap::previous_tempo_section (TempoSection* ts) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return previous_tempo_section_locked (_metrics, ts);
+
+}
+
+TempoSection*
+TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
+{
+       if (!ts) {
+               return 0;
+       }
+
+       TempoSection* prev = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+
+               if ((*i)->is_tempo()) {
+                       TempoSection* t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
+                       if (prev && t == ts) {
+
+                               return prev;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       return 0;
+}
+
+TempoSection*
+TempoMap::next_tempo_section (TempoSection* ts) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return next_tempo_section_locked (_metrics, ts);
+}
+
+TempoSection*
+TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
+{
+       if (!ts) {
+               return 0;
+       }
+
+       TempoSection* prev = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+
+               if ((*i)->is_tempo()) {
+                       TempoSection* t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
+                       if (prev && prev == ts) {
+
+                               return t;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       return 0;
+}
 /* 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
 /* 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 (const framepos_t& frame, const framecnt_t& sr) const
+TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
@@ -3564,16 +4464,24 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
                        ts_at = t;
                }
        }
                        ts_at = t;
                }
        }
+       assert (ts_at);
 
        if (ts_after) {
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * 4.0);
+               return  (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
        }
        /* must be treated as constant tempo */
        }
        /* must be treated as constant tempo */
-       return ts_at->frames_per_beat (_frame_rate);
+       return ts_at->frames_per_quarter_note (_frame_rate);
+}
+
+const MeterSection&
+TempoMap::meter_section_at_frame (framepos_t frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 const MeterSection&
 }
 
 const MeterSection&
-TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
+TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
 {
        Metrics::const_iterator i;
        MeterSection* prev = 0;
 {
        Metrics::const_iterator i;
        MeterSection* prev = 0;
@@ -3585,7 +4493,7 @@ TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t fram
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
 
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
 
-                       if (prev && (*i)->frame() > frame) {
+                       if (prev && (*i)->minute() > minute) {
                                break;
                        }
 
                                break;
                        }
 
@@ -3601,14 +4509,6 @@ TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t fram
        return *prev;
 }
 
        return *prev;
 }
 
-
-const MeterSection&
-TempoMap::meter_section_at_frame (framepos_t frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return meter_section_at_frame_locked (_metrics, frame);
-}
-
 const MeterSection&
 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
 const MeterSection&
 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
@@ -3647,17 +4547,18 @@ TempoMap::fix_legacy_session ()
 {
        MeterSection* prev_m = 0;
        TempoSection* prev_t = 0;
 {
        MeterSection* prev_m = 0;
        TempoSection* prev_t = 0;
+       bool have_initial_t = false;
 
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                MeterSection* m;
                TempoSection* t;
 
                if ((m = dynamic_cast<MeterSection*>(*i)) != 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()) {
+                       if (m->initial()) {
                                pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                m->set_beat (bbt);
                                m->set_pulse (0.0);
                                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_minute (0.0);
                                m->set_position_lock_style (AudioTime);
                                prev_m = m;
                                continue;
                                m->set_position_lock_style (AudioTime);
                                prev_m = m;
                                continue;
@@ -3680,15 +4581,26 @@ TempoMap::fix_legacy_session ()
                                continue;
                        }
 
                                continue;
                        }
 
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                t->set_pulse (0.0);
                                t->set_pulse (0.0);
-                               t->set_frame (0);
+                               t->set_minute (0.0);
                                t->set_position_lock_style (AudioTime);
                                prev_t = t;
                                t->set_position_lock_style (AudioTime);
                                prev_t = t;
+                               have_initial_t = true;
                                continue;
                        }
 
                        if (prev_t) {
                                continue;
                        }
 
                        if (prev_t) {
+                               /* some 4.x sessions have no initial (non-movable) tempo. */
+                               if (!have_initial_t) {
+                                       prev_t->set_pulse (0.0);
+                                       prev_t->set_minute (0.0);
+                                       prev_t->set_position_lock_style (AudioTime);
+                                       prev_t->set_initial (true);
+                                       prev_t->set_locked_to_meter (true);
+                                       have_initial_t = true;
+                               }
+
                                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);
                                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);
@@ -3703,6 +4615,30 @@ TempoMap::fix_legacy_session ()
                }
        }
 }
                }
        }
 }
+void
+TempoMap::fix_legacy_end_session ()
+{
+       TempoSection* prev_t = 0;
+
+       for (Metrics::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 (prev_t->type() != TempoSection::Constant) {
+                                       prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
+                               }
+                       }
+
+                       prev_t = t;
+               }
+       }
+}
 
 XMLNode&
 TempoMap::get_state ()
 
 XMLNode&
 TempoMap::get_state ()
@@ -3739,7 +4675,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        if (child->name() == TempoSection::xml_state_node_name) {
 
                                try {
                        if (child->name() == TempoSection::xml_state_node_name) {
 
                                try {
-                                       TempoSection* ts = new TempoSection (*child);
+                                       TempoSection* ts = new TempoSection (*child, _frame_rate);
                                        _metrics.push_back (ts);
                                }
 
                                        _metrics.push_back (ts);
                                }
 
@@ -3753,7 +4689,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        } else if (child->name() == MeterSection::xml_state_node_name) {
 
                                try {
                        } else if (child->name() == MeterSection::xml_state_node_name) {
 
                                try {
-                                       MeterSection* ms = new MeterSection (*child);
+                                       MeterSection* ms = new MeterSection (*child, _frame_rate);
                                        _metrics.push_back (ms);
                                }
 
                                        _metrics.push_back (ms);
                                }
 
@@ -3779,6 +4715,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                                        fix_legacy_session();
                                        break;
                                }
                                        fix_legacy_session();
                                        break;
                                }
+
+                               if (t->legacy_end()) {
+                                       fix_legacy_end_session();
+                                       break;
+                               }
+
                                break;
                        }
                }
                                break;
                        }
                }
@@ -3827,27 +4769,36 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 }
 
 void
 }
 
 void
-TempoMap::dump (const Metrics& metrics, std::ostream& o) const
+TempoMap::dump (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;
 
 {
        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) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       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;
+                       o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
+                         << " type= " << enum_2_string (t->type()) << ") "  << " at pulse= " << t->pulse()
+                         << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
+                         << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
                        if (prev_t) {
                        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;
+                               o <<  "  current start  : " << t->note_types_per_minute()
+                                 <<  "  current end  : " << t->end_note_types_per_minute()
+                                 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
+                               o << "  previous     : " << prev_t->note_types_per_minute()
+                                 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
+                               o << "  calculated   : " << prev_t->tempo_at_pulse (t->pulse())
+                                 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
+                                 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
+                                 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
                        }
                        prev_t = t;
                } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
                        }
                        prev_t = t;
                } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
-                         << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
+                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
+                         << " frame= " << m->frame() << " pulse: " << m->pulse() <<  " beat : " << m->beat()
+                         << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
                }
        }
        o << "------" << std::endl;
                }
        }
        o << "------" << std::endl;
@@ -3886,129 +4837,24 @@ TempoMap::n_meters() const
 void
 TempoMap::insert_time (framepos_t where, framecnt_t amount)
 {
 void
 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) {
-                       if ((*i)->frame() >= where && (*i)->movable ()) {
-                               (*i)->set_frame ((*i)->frame() + amount);
-                       }
-               }
-
-               /* now reset the BBT time of all metrics, based on their new
-                * audio time. This is the only place where we do this reverse
-                * timestamp.
-                */
-
-               Metrics::iterator i;
-               const MeterSection* meter;
-               const TempoSection* tempo;
-               MeterSection *m;
-               TempoSection *t;
-
-               meter = &first_meter ();
-               tempo = &first_tempo ();
-
-               BBT_Time start;
-               BBT_Time end;
-
-               // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
-
-               bool first = true;
-               MetricSection* prev = 0;
-
-               for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
+               if ((*i)->frame() >= where && !(*i)->initial ()) {
+                       MeterSection* ms;
+                       TempoSection* ts;
 
 
-                       BBT_Time bbt;
-                       //TempoMetric metric (*meter, *tempo);
-                       MeterSection* ms = const_cast<MeterSection*>(meter);
-                       TempoSection* ts = const_cast<TempoSection*>(tempo);
-                       if (prev) {
-                               if (ts){
-                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               if (!t->active()) {
-                                                       continue;
-                                               }
-                                               ts->set_pulse (t->pulse());
-                                       }
-                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               ts->set_pulse (m->pulse());
-                                       }
-                                       ts->set_frame (prev->frame());
-
-                               }
-                               if (ms) {
-                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
-                                               ms->set_beat (start);
-                                               ms->set_pulse (m->pulse());
-                                       }
-                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               if (!t->active()) {
-                                                       continue;
-                                               }
-                                               const double beat = beat_at_pulse_locked (_metrics, t->pulse());
-                                               pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
-                                               ms->set_beat (start);
-                                               ms->set_pulse (t->pulse());
-                                       }
-                                       ms->set_frame (prev->frame());
-                               }
-
-                       } else {
-                               // metric will be at frames=0 bbt=1|1|0 by default
-                               // which is correct for our purpose
+                       if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
+                               gui_set_meter_position (ms, (*i)->frame() + amount);
                        }
 
                        }
 
-                       // cerr << bbt << endl;
-
-                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               if (!t->active()) {
-                                       continue;
-                               }
-                               t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
-                               tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
-                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               bbt = bbt_at_frame_locked (_metrics, m->frame());
-
-                               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
-
-                               if (first) {
-                                       first = false;
-                               } else {
-
-                                       if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
-                                               /* round up to next beat */
-                                               bbt.beats += 1;
-                                       }
-
-                                       bbt.ticks = 0;
-
-                                       if (bbt.beats != 1) {
-                                               /* round up to next bar */
-                                               bbt.bars += 1;
-                                               bbt.beats = 1;
-                                       }
-                               }
-                               pair<double, BBT_Time> start = make_pair (max (0.0, 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)->pulse() <<endl;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
+                       if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
+                               gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
                        }
                        }
-
-                       prev = (*i);
                }
                }
-
-               recompute_map (_metrics);
        }
 
        }
 
-
        PropertyChanged (PropertyChange ());
 }
        PropertyChanged (PropertyChange ());
 }
+
 bool
 TempoMap::remove_time (framepos_t where, framecnt_t amount)
 {
 bool
 TempoMap::remove_time (framepos_t where, framecnt_t amount)
 {
@@ -4034,7 +4880,7 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
                        }
                        else if ((*i)->frame() >= where) {
                                // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
                        }
                        else if ((*i)->frame() >= where) {
                                // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
-                               (*i)->set_frame ((*i)->frame() - amount);
+                               (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
                                if ((*i)->frame() == where) {
                                        // marker was immediately after end of range
                                        tempo_after = dynamic_cast<TempoSection*> (*i);
                                if ((*i)->frame() == where) {
                                        // marker was immediately after end of range
                                        tempo_after = dynamic_cast<TempoSection*> (*i);
@@ -4047,12 +4893,12 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
                //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
                if (last_tempo && !tempo_after) {
                        metric_kill_list.remove(last_tempo);
                //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
                if (last_tempo && !tempo_after) {
                        metric_kill_list.remove(last_tempo);
-                       last_tempo->set_frame(where);
+                       last_tempo->set_minute (minute_at_frame (where));
                        moved = true;
                }
                if (last_meter && !meter_after) {
                        metric_kill_list.remove(last_meter);
                        moved = true;
                }
                if (last_meter && !meter_after) {
                        metric_kill_list.remove(last_meter);
-                       last_meter->set_frame(where);
+                       last_meter->set_minute (minute_at_frame (where));
                        moved = true;
                }
 
                        moved = true;
                }
 
@@ -4070,40 +4916,24 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
        return moved;
 }
 
        return moved;
 }
 
-/** Add some (fractional) beats to a session frame position, and return the result in frames.
+/** Add some (fractional) Beats to a session frame position, and return the result in frames.
  *  pos can be -ve, if required.
  */
 framepos_t
  *  pos can be -ve, if required.
  */
 framepos_t
-TempoMap::framepos_plus_beats (framepos_t frame, Evoral::Beats beats) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double());
-}
-framepos_t
-TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return frame_at_quarter_note_locked (_metrics, quarter_note_at_frame_locked (_metrics, frame) + quarter_note.to_double());
-}
-
-/** Subtract some (fractional) beats from a frame position, and return the result in frames */
-framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
+TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
 
 
-       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
+       return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
 }
 
 }
 
-/** Add the BBT interval op to pos and return the result */
 framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
+       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
        pos_bbt.ticks += op.ticks;
        if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
                ++pos_bbt.beats;
        pos_bbt.ticks += op.ticks;
        if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
                ++pos_bbt.beats;
@@ -4119,27 +4949,20 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
        pos_bbt.bars += op.bars;
 
        }
        pos_bbt.bars += op.bars;
 
-       return frame_at_bbt_locked (_metrics, pos_bbt);
+       return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
     starting at pos.
 */
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
     starting at pos.
 */
-Evoral::Beats
-TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
-}
-
 Evoral::Beats
 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 Evoral::Beats
 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return Evoral::Beats (quarter_note_at_frame_locked (_metrics, pos + distance) - quarter_note_at_frame_locked (_metrics, pos));
+       return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
 }
 }
+
 struct bbtcmp {
     bool operator() (const BBT_Time& a, const BBT_Time& b) {
            return a < b;
 struct bbtcmp {
     bool operator() (const BBT_Time& a, const BBT_Time& b) {
            return a < b;
@@ -4153,7 +4976,7 @@ operator<< (std::ostream& o, const Meter& m) {
 
 std::ostream&
 operator<< (std::ostream& o, const Tempo& t) {
 
 std::ostream&
 operator<< (std::ostream& o, const Tempo& t) {
-       return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
+       return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
 }
 
 std::ostream&
 }
 
 std::ostream&