Fix log-scale rangesteps and log-control numeric spinboxes
[ardour.git] / libs / ardour / tempo.cc
index 07c008ab7ce8a0d805047f170236cf55a05a20b3..ce2d311d391c475aa407395e29e27b119cd72c04 100644 (file)
 
 #include "pbd/enumwriter.h"
 #include "pbd/xml++.h"
 
 #include "pbd/enumwriter.h"
 #include "pbd/xml++.h"
-#include "evoral/Beats.hpp"
+
+#include "temporal/beats.h"
 
 #include "ardour/debug.h"
 #include "ardour/lmath.h"
 #include "ardour/tempo.h"
 
 #include "ardour/debug.h"
 #include "ardour/lmath.h"
 #include "ardour/tempo.h"
+#include "ardour/types_convert.h"
 
 #include "pbd/i18n.h"
 #include <locale.h>
 
 #include "pbd/i18n.h"
 #include <locale.h>
@@ -45,24 +47,52 @@ 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
+samplepos_t
+MetricSection::sample_at_minute (const double& time) const
 {
 {
-       return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
+       return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
 }
 
 double
 }
 
 double
-MetricSection::minute_at_frame (const framepos_t& frame) const
+MetricSection::minute_at_sample (const samplepos_t sample) const
+{
+       return (sample / (double) _sample_rate) / 60.0;
+}
+
+/***********************************************************************/
+
+bool
+ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str)
+{
+       char buf[256];
+       int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
+                              bbt.ticks);
+
+       if (retval <= 0 || retval >= (int)sizeof(buf)) {
+               return false;
+       }
+
+       str = buf;
+       return true;
+}
+
+bool
+ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt)
 {
 {
-       return (frame / (double) _sample_rate) / 60.0;
+       if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
+                   &bbt.ticks) == 3) {
+               return true;
+       }
+       return false;
 }
 
 }
 
+
 /***********************************************************************/
 
 double
 /***********************************************************************/
 
 double
-Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
+Meter::samples_per_grid (const Tempo& tempo, samplecnt_t sr) const
 {
        /* This is tempo- and meter-sensitive. The number it returns
           is based on the interval between any two lines in the
 {
        /* This is tempo- and meter-sensitive. The number it returns
           is based on the interval between any two lines in the
@@ -71,125 +101,138 @@ 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
-Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
+Meter::samples_per_bar (const Tempo& tempo, samplecnt_t sr) const
+{
+       return samples_per_grid (tempo, sr) * _divisions_per_bar;
+}
+
+/***********************************************************************/
+
+void
+MetricSection::add_state_to_node(XMLNode& node) const
+{
+       node.set_property ("pulse", _pulse);
+       node.set_property ("frame", sample());
+       node.set_property ("movable", !_initial);
+       node.set_property ("lock-style", _position_lock_style);
+}
+
+int
+MetricSection::set_state (const XMLNode& node, int /*version*/)
 {
 {
-       return frames_per_grid (tempo, sr) * _divisions_per_bar;
+       node.get_property ("pulse", _pulse);
+
+       samplepos_t sample;
+       if (node.get_property ("frame", sample)) {
+               set_minute (minute_at_sample (sample));
+       }
+
+       bool tmp;
+       if (!node.get_property ("movable", tmp)) {
+               error << _("TempoSection XML node has no \"movable\" property") << endmsg;
+               throw failed_constructor();
+       }
+       _initial = !tmp;
+
+       if (!node.get_property ("lock-style", _position_lock_style)) {
+               if (!initial()) {
+                       _position_lock_style = MusicTime;
+               } else {
+                       _position_lock_style = AudioTime;
+               }
+       }
+       return 0;
 }
 
 /***********************************************************************/
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
 }
 
 /***********************************************************************/
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
-TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
+TempoSection::TempoSection (const XMLNode& node, samplecnt_t sample_rate)
        : MetricSection (0.0, 0, MusicTime, true, sample_rate)
        , Tempo (TempoMap::default_tempo())
        : MetricSection (0.0, 0, MusicTime, true, sample_rate)
        , 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)
 {
 {
-       XMLProperty const * prop;
-       LocaleGuard lg;
        BBT_Time bbt;
        BBT_Time bbt;
-       double pulse;
-       uint32_t frame;
-       double minute;
-
-       _legacy_bbt = BBT_Time (0, 0, 0);
-
-       if ((prop = node.property ("start")) != 0) {
-               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                           &bbt.bars,
-                           &bbt.beats,
-                           &bbt.ticks) == 3) {
+       std::string start_bbt;
+       _legacy_bbt.bars = 0; // legacy session check compars .bars != 0; default BBT_Time c'tor uses 1.
+       if (node.get_property ("start", start_bbt)) {
+               if (string_to_bbt_time (start_bbt, bbt)) {
                        /* legacy session - start used to be in bbt*/
                        _legacy_bbt = bbt;
                        /* legacy session - start used to be in bbt*/
                        _legacy_bbt = bbt;
-                       pulse = -1.0;
+                       set_pulse(-1.0);
                        info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
                }
        }
 
                        info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
                }
        }
 
-       if ((prop = node.property ("pulse")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
-                       error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
-               }
-       }
-
-       set_pulse (pulse);
+       // Don't worry about return value, exception will be thrown on error
+       MetricSection::set_state (node, Stateful::loading_state_version);
 
 
-       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;
-               } else {
-                       set_minute (minute_at_frame (frame));
+       if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
+               if (_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 ("minute")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
-                       error << _("TempoSection XML node has an illegal \"minute\" value") << endmsg;
-               } else {
-                       set_minute (minute);
+       if (node.get_property ("note-type", _note_type)) {
+               if (_note_type < 1.0) {
+                       error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
+                       throw failed_constructor();
                }
                }
-       }
-
-       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();
-       }
-
-       if ((prop = node.property ("note-type")) == 0) {
+       } else {
                /* older session, make note type be quarter by default */
                _note_type = 4.0;
                /* older session, make note type be quarter by default */
                _note_type = 4.0;
-       } else {
-               if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
-                       error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
-                       throw failed_constructor();
        }
        }
+
+       if (!node.get_property ("clamped", _clamped)) {
+               _clamped = false;
        }
 
        }
 
-       if ((prop = node.property ("movable")) == 0) {
-               error << _("TempoSection XML node has no \"movable\" property") << endmsg;
-               throw failed_constructor();
+       if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
+               if (_end_note_types_per_minute < 0.0) {
+                       info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
+                       throw failed_constructor();
+               }
        }
 
        }
 
-       set_movable (string_is_affirmative (prop->value()));
+       TempoSection::Type old_type;
+       if (node.get_property ("tempo-type", old_type)) {
+               /* sessions with a tempo-type node contain no end-beats-per-minute.
+                  if the legacy node indicates a constant tempo, simply fill this in with the
+                  start tempo. otherwise we need the next neighbour to know what it will be.
+               */
 
 
-       if ((prop = node.property ("active")) == 0) {
-               warning << _("TempoSection XML node has no \"active\" property") << endmsg;
-               set_active(true);
-       } else {
-               set_active (string_is_affirmative (prop->value()));
+               if (old_type == TempoSection::Constant) {
+                       _end_note_types_per_minute = _note_types_per_minute;
+               } else {
+                       _end_note_types_per_minute = -1.0;
+               }
        }
 
        }
 
-       if ((prop = node.property ("tempo-type")) == 0) {
-               _type = Constant;
-       } else {
-               _type = Type (string_2_enum (prop->value(), _type));
+       if (!node.get_property ("active", _active)) {
+               warning << _("TempoSection XML node has no \"active\" property") << endmsg;
+               _active = true;
        }
 
        }
 
-       if ((prop = node.property ("lock-style")) == 0) {
-               if (movable()) {
-                       set_position_lock_style (MusicTime);
+       if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
+               if (initial()) {
+                       set_locked_to_meter (true);
                } else {
                } else {
-                       set_position_lock_style (AudioTime);
+                       set_locked_to_meter (false);
                }
                }
-       } else {
-               set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
        }
 
        }
 
-       if ((prop = node.property ("locked-to-meter")) == 0) {
-               set_locked_to_meter (false);
-       } else {
-               set_locked_to_meter (string_is_affirmative (prop->value()));
+       /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
+       if (initial()) {
+               set_locked_to_meter (true);
        }
 }
 
        }
 }
 
@@ -197,115 +240,147 @@ XMLNode&
 TempoSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
 TempoSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
-       char buf[256];
-       LocaleGuard lg;
-
-       snprintf (buf, sizeof (buf), "%lf", pulse());
-       root->add_property ("pulse", buf);
-       snprintf (buf, sizeof (buf), "%li", frame());
-       root->add_property ("frame", buf);
-       snprintf (buf, sizeof (buf), "%lf", minute());
-       root->add_property ("minute", buf);
-       snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
-       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");
-       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;
-}
+       MetricSection::add_state_to_node (*root);
 
 
-void
-TempoSection::set_type (Type type)
-{
-       _type = type;
+       root->set_property ("beats-per-minute", _note_types_per_minute);
+       root->set_property ("note-type", _note_type);
+       root->set_property ("clamped", _clamped);
+       root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
+       root->set_property ("active", _active);
+       root->set_property ("locked-to-meter", _locked_to_meter);
+
+       return *root;
 }
 
 }
 
-/** returns the tempo in beats per minute at the zero-based (relative to session) minute.
+/** returns the Tempo at the session-relative minute.
 */
 */
-double
+Tempo
 TempoSection::tempo_at_minute (const double& m) const
 {
 TempoSection::tempo_at_minute (const double& m) const
 {
-
-       if (_type == Constant || _c_func == 0.0) {
-               return beats_per_minute();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
+       if (constant) {
+               return Tempo (note_types_per_minute(), note_type());
        }
 
        }
 
-       return _tempo_at_time (m - minute());
+       return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
 }
 
 }
 
-/** returns the zero-based minute (relative to session)
-   where the tempo in beats per minute occurs in this section.
-   pulse p is only used for constant tempi.
-   note that the tempo map may have multiple such values.
+/** 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::minute_at_tempo (const double& bpm, const double& p) const
+TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return (((p - pulse())  * note_type()) / beats_per_minute()) + minute();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return ((p - pulse()) / pulses_per_minute()) + minute();
        }
 
        }
 
-       return _time_at_tempo (bpm) + minute();
+       return _time_at_tempo (ntpm) + minute();
 }
 }
-/** returns the tempo in beats per minute at the supplied pulse.
-*/
-double
+
+/** returns the Tempo at the supplied whole-note pulse.
+ */
+Tempo
 TempoSection::tempo_at_pulse (const double& p) const
 {
 TempoSection::tempo_at_pulse (const double& p) const
 {
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
 
 
-       if (_type == Constant || _c_func == 0.0) {
-               return beats_per_minute();
+       if (constant) {
+               return Tempo (note_types_per_minute(), note_type());
        }
 
        }
 
-       return _tempo_at_pulse (p - pulse());
+       return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
 }
 
 }
 
-/** returns the pulse where the tempo in beats per minute occurs given frame f.
-    frame f is only used for constant tempi.
-    note that the session tempo map may have multiple locations where a given tempo occurs.
+/** 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::pulse_at_tempo (const double& bpm, const double& m) const
+TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
 {
 {
-       if (_type == Constant || _c_func == 0.0) {
-               const double pulses = (((m - minute()) * beats_per_minute()) / note_type()) + 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_tempo (bpm) + pulse();
+       return _pulse_at_tempo (ntpm) + pulse();
 }
 
 }
 
-/** returns the pulse at the supplied session-relative minute.
+/** returns the whole-note pulse at the supplied session-relative minute.
 */
 double
 TempoSection::pulse_at_minute (const double& m) const
 {
 */
 double
 TempoSection::pulse_at_minute (const double& m) const
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return (((m - minute()) * beats_per_minute()) / _note_type) + pulse();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
+       if (constant) {
+               return ((m - minute()) * pulses_per_minute()) + pulse();
        }
 
        return _pulse_at_time (m - minute()) + pulse();
 }
 
        }
 
        return _pulse_at_time (m - minute()) + pulse();
 }
 
-/** returns the minute (relative to session start) at the supplied pulse.
+/** returns the session-relative minute at the supplied whole-note pulse.
 */
 double
 TempoSection::minute_at_pulse (const double& p) const
 {
 */
 double
 TempoSection::minute_at_pulse (const double& p) const
 {
-       if (_type == Constant || _c_func == 0.0) {
-               return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return ((p - pulse()) / pulses_per_minute()) + minute();
        }
 
        return _time_at_pulse (p - pulse()) + minute();
 }
 
        }
 
        return _time_at_pulse (p - pulse()) + minute();
 }
 
+/** returns thw whole-note pulse at session sample position f.
+ *  @param f the sample position.
+ *  @return the position in whole-note pulses corresponding to f
+ *
+ *  for use with musical units whose granularity is coarser than samples (e.g. ticks)
+*/
+double
+TempoSection::pulse_at_sample (const samplepos_t f) const
+{
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && f < sample());
+       if (constant) {
+               return (minute_at_sample (f - sample()) * pulses_per_minute()) + pulse();
+       }
+
+       return _pulse_at_time (minute_at_sample (f - sample())) + pulse();
+}
+
+samplepos_t
+TempoSection::sample_at_pulse (const double& p) const
+{
+       const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
+       if (constant) {
+               return sample_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
+       }
+
+       return sample_at_minute (_time_at_pulse (p - pulse()) + minute());
+}
+
 /*
 Ramp Overview
 
 /*
 Ramp Overview
 
@@ -381,220 +456,167 @@ https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Te
 
 */
 
 
 */
 
-/*
-  compute this ramp's function constant using the end tempo (in qn beats 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
+TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
 {
 {
-       double const log_tempo_ratio = log (end_bpm / beats_per_minute());
-       return (beats_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
+       if (note_types_per_minute() == end_npm || type() == Constant) {
+               return 0.0;
+       }
+
+       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 later tempo section, given tempo (quarter notes/min.) and distance (in frames) from session origin */
+/** 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::compute_c_func_minute (const double& end_bpm, const double& end_minute) const
+TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
 {
 {
-       return c_func (end_bpm, end_minute - minute());
+       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_bpm, double c_func) const
+TempoSection::a_func (double end_npm, double c) const
 {
 {
-       return log (end_bpm / beats_per_minute()) / c_func;
+       return log (end_npm / note_types_per_minute()) / c;
 }
 
 /*function constant*/
 double
 }
 
 /*function constant*/
 double
-TempoSection::c_func (double end_bpm, double end_time) const
+TempoSection::c_func (double end_npm, double end_time) const
 {
 {
-       return log (end_bpm / beats_per_minute()) / end_time;
+       return log (end_npm / note_types_per_minute()) / end_time;
 }
 
 }
 
-/* tempo in bpm at time in minutes */
+/* tempo in note types per minute at time in minutes */
 double
 TempoSection::_tempo_at_time (const double& time) const
 {
 double
 TempoSection::_tempo_at_time (const double& time) const
 {
-       return exp (_c_func * time) * beats_per_minute();
+       return exp (_c * time) * note_types_per_minute();
 }
 
 }
 
-/* time in minutes at tempo in bpm */
+/* time in minutes at tempo in note types per minute */
 double
 double
-TempoSection::_time_at_tempo (const double& tempo) const
+TempoSection::_time_at_tempo (const double& npm) const
 {
 {
-       return log (tempo / beats_per_minute()) / _c_func;
+       return log (npm / note_types_per_minute()) / _c;
 }
 
 }
 
-/* pulse at tempo in bpm */
+/* pulse at tempo in note types per minute */
 double
 double
-TempoSection::_pulse_at_tempo (const double& tempo) const
+TempoSection::_pulse_at_tempo (const double& npm) const
 {
 {
-       return ((tempo - beats_per_minute()) / _c_func) / _note_type;
+       return ((npm - note_types_per_minute()) / _c) / _note_type;
 }
 
 }
 
-/* tempo in bpm at pulse */
+/* tempo in note types per minute at pulse */
 double
 TempoSection::_tempo_at_pulse (const double& pulse) const
 {
 double
 TempoSection::_tempo_at_pulse (const double& pulse) const
 {
-       return (pulse * _note_type * _c_func) + beats_per_minute();
+       return (pulse * _note_type * _c) + note_types_per_minute();
 }
 
 /* pulse at time in minutes */
 double
 TempoSection::_pulse_at_time (const double& time) const
 {
 }
 
 /* pulse at time in minutes */
 double
 TempoSection::_pulse_at_time (const double& time) const
 {
-       return expm1 (_c_func * time) * (beats_per_minute() / (_c_func * _note_type));
+       return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
 }
 
 /* time in minutes at pulse */
 double
 TempoSection::_time_at_pulse (const double& pulse) const
 {
 }
 
 /* time in minutes at pulse */
 double
 TempoSection::_time_at_pulse (const double& pulse) const
 {
-       return log1p ((_c_func * pulse * _note_type) / beats_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, const framecnt_t sample_rate)
+MeterSection::MeterSection (const XMLNode& node, const samplecnt_t sample_rate)
        : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
 {
        : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
 {
-       XMLProperty const * prop;
-       LocaleGuard lg;
-       BBT_Time bbt;
-       double pulse = 0.0;
-       double beat = 0.0;
-       framepos_t frame = 0;
        pair<double, BBT_Time> start;
        pair<double, BBT_Time> start;
-       double minute = 0.0;
+       start.first = 0.0;
 
 
-       if ((prop = node.property ("start")) != 0) {
-               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &bbt.bars,
-                   &bbt.beats,
-                   &bbt.ticks) < 3) {
-                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
-               } else {
+       std::string bbt_str;
+       if (node.get_property ("start", bbt_str)) {
+               if (string_to_bbt_time (bbt_str, start.second)) {
                        /* legacy session - start used to be in bbt*/
                        info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
                        /* legacy session - start used to be in bbt*/
                        info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
-                       pulse = -1.0;
-               }
-       }
-
-       if ((prop = node.property ("pulse")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
-                       error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
+                       set_pulse (-1.0);
+               } else {
+                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
                }
        }
                }
        }
-       set_pulse (pulse);
 
 
-       if ((prop = node.property ("beat")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
-                       error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
-               }
-       }
+       MetricSection::set_state (node, Stateful::loading_state_version);
 
 
-       start.first = beat;
+       node.get_property ("beat", start.first);
 
 
-       if ((prop = node.property ("bbt")) == 0) {
+       if (node.get_property ("bbt", bbt_str)) {
+               if (!string_to_bbt_time (bbt_str, start.second)) {
+                       error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
+                       throw failed_constructor();
+               }
+       } else {
                warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
                warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
-       } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &bbt.bars,
-                   &bbt.beats,
-                   &bbt.ticks) < 3) {
-               error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
-               throw failed_constructor();
        }
 
        }
 
-       start.second = bbt;
        set_beat (start);
 
        set_beat (start);
 
-       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;
-               } else {
-                       set_minute (minute_at_frame (frame));
-               }
-       }
-       if ((prop = node.property ("minute")) != 0) {
-               if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
-                       error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
-               } else {
-                       set_minute (minute);
-               }
-       }
-
        /* beats-per-bar is old; divisions-per-bar is new */
 
        /* beats-per-bar is old; divisions-per-bar is new */
 
-       if ((prop = node.property ("divisions-per-bar")) == 0) {
-               if ((prop = node.property ("beats-per-bar")) == 0) {
+       if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
+               if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
                        error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
                        throw failed_constructor();
                }
        }
                        error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
                        throw failed_constructor();
                }
        }
-       if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
+
+       if (_divisions_per_bar < 0.0) {
                error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
                throw failed_constructor();
        }
 
                error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
                throw failed_constructor();
        }
 
-       if ((prop = node.property ("note-type")) == 0) {
+       if (!node.get_property ("note-type", _note_type)) {
                error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
                throw failed_constructor();
        }
                error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
                throw failed_constructor();
        }
-       if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
-               error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
-               throw failed_constructor();
-       }
 
 
-       if ((prop = node.property ("movable")) == 0) {
-               error << _("MeterSection XML node has no \"movable\" property") << endmsg;
+       if (_note_type < 0.0) {
+               error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
                throw failed_constructor();
        }
                throw failed_constructor();
        }
-
-       set_movable (string_is_affirmative (prop->value()));
-
-       if ((prop = node.property ("lock-style")) == 0) {
-               warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
-               if (movable()) {
-                       set_position_lock_style (MusicTime);
-               } else {
-                       set_position_lock_style (AudioTime);
-               }
-       } else {
-               set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
-       }
 }
 
 XMLNode&
 MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
 }
 
 XMLNode&
 MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
-       char buf[256];
-       LocaleGuard lg;
-
-       snprintf (buf, sizeof (buf), "%lf", pulse());
-       root->add_property ("pulse", buf);
-       snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 bbt().bars,
-                 bbt().beats,
-                 bbt().ticks);
-       root->add_property ("bbt", buf);
-       snprintf (buf, sizeof (buf), "%lf", beat());
-       root->add_property ("beat", buf);
-       snprintf (buf, sizeof (buf), "%lf", _note_type);
-       root->add_property ("note-type", buf);
-       snprintf (buf, sizeof (buf), "%li", frame());
-       root->add_property ("frame", buf);
-       snprintf (buf, sizeof (buf), "%lf", minute());
-       root->add_property ("minute", 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");
-       root->add_property ("movable", buf);
+
+       MetricSection::add_state_to_node (*root);
+
+       std::string bbt_str;
+       bbt_time_to_string (_bbt, bbt_str);
+       root->set_property ("bbt", bbt_str);
+       root->set_property ("beat", beat());
+       root->set_property ("note-type", _note_type);
+       root->set_property ("divisions-per-bar", _divisions_per_bar);
 
        return *root;
 }
 
        return *root;
 }
@@ -603,88 +625,82 @@ 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).
+  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.
 
 
-  MeterSecion - divides pulse into measures (via divisions_per_bar) and beats (via note_divisor).
-
-  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 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 lock style determines the location type to be kept as a reference when location is recalculated.
 
 
-  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.
+  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.
 
 
-  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.
+  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).
 
 
-  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,
   reordering is frequent. Care must be taken to keep _metrics in a solved state.
 
   It is important to keep the _metrics in an order that makes sense.
   Because ramped MusicTime and AudioTime tempos can interact with each other,
   reordering is frequent. Care must be taken to keep _metrics in a solved state.
-  Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
+  Solved means ordered by sample or pulse with sample-accurate precision (see check_solved()).
 
   Music and Audio
 
   Music and audio-locked objects may seem interchangeable on the surface, but when translating
   between audio samples and beat, remember that a sample is only a quantised approximation
   of the actual time (in minutes) of a beat.
 
   Music and Audio
 
   Music and audio-locked objects may seem interchangeable on the surface, but when translating
   between audio samples and beat, remember that a sample is only a quantised approximation
   of the actual time (in minutes) of a beat.
-  Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
-  mean that this frame is the actual location in time of 1|3|0.
+  Thus if a gui user points to the sample occupying the start of a music-locked object on 1|3|0, it does not
+  mean that this sample is the actual location in time of 1|3|0.
 
 
-  You cannot use a frame measurement to determine beat distance except under special circumstances
-  (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
+  You cannot use a sample measurement to determine beat distance except under special circumstances
+  (e.g. where the user has requested that a beat lie on a SMPTE sample or if the tempo is known to be constant over the duration).
 
   This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
   sample space the user is operating at to be translated correctly to the object.
 
 
   This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
   sample space the user is operating at to be translated correctly to the object.
 
-  The current approach is to interpret the supplied frame using the grid division the user has currently selected.
-  If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
-  the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
+  The current approach is to interpret the supplied sample using the grid division the user has currently selected.
+  If the user has no musical grid set, they are actually operating in sample space (even SMPTE samples are rounded to audio sample), so
+  the supplied audio sample is interpreted as the desired musical location (beat_at_sample()).
 
   tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
 
 
   tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
 
-  When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
-  The result is rounded to audio frames.
-  When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
+  When sample_at_beat() is called, the position calculation is performed in pulses and minutes.
+  The result is rounded to audio samples.
+  When beat_at_sample() is called, the sample is converted to minutes, with no rounding performed on the result.
 
   So :
 
   So :
-  frame_at_beat (beat_at_frame (frame)) == frame
+  sample_at_beat (beat_at_sample (sample)) == sample
   but :
   but :
-  beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
+  beat_at_sample (sample_at_beat (beat)) != beat due to the time quantization of sample_at_beat().
 
   Doing the second one will result in a beat distance error of up to 0.5 audio samples.
 
   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.
+  samples_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 samples last.
 
 
-  The above pointless example would then do:
-  beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
+  The above pointless example could instead do:
+  beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
+
+  The Shaggs - Things I Wonder
+  https://www.youtube.com/watch?v=9wQK6zMJOoQ
 
 */
 struct MetricSectionSorter {
 
 */
 struct MetricSectionSorter {
@@ -695,28 +711,64 @@ struct MetricSectionSorter {
 
 struct MetricSectionFrameSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
 
 struct MetricSectionFrameSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->frame() < b->frame();
+           return a->sample() < b->sample();
     }
 };
 
     }
 };
 
-TempoMap::TempoMap (framecnt_t fr)
+TempoMap::TempoMap (samplecnt_t fr)
 {
 {
-       _frame_rate = fr;
+       _sample_rate = fr;
        BBT_Time start (1, 1, 0);
 
        BBT_Time start (1, 1, 0);
 
-       TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
+       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);
 
        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: sample time is correct (zero) for both of these */
 
        _metrics.push_back (t);
        _metrics.push_back (m);
 
 }
 
 
        _metrics.push_back (t);
        _metrics.push_back (m);
 
 }
 
+TempoMap&
+TempoMap::operator= (TempoMap const & other)
+{
+       if (&other != this) {
+               Glib::Threads::RWLock::ReaderLock lr (other.lock);
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               _sample_rate = other._sample_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 const * const ts = dynamic_cast<TempoSection const * const> (*m);
+                       MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*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();
@@ -727,16 +779,16 @@ TempoMap::~TempoMap ()
        _metrics.clear();
 }
 
        _metrics.clear();
 }
 
-framepos_t
-TempoMap::frame_at_minute (const double time) const
+samplepos_t
+TempoMap::sample_at_minute (const double time) const
 {
 {
-       return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
+       return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
 }
 
 double
 }
 
 double
-TempoMap::minute_at_frame (const framepos_t frame) const
+TempoMap::minute_at_sample (const samplepos_t sample) const
 {
 {
-       return (frame / (double) _frame_rate) / 60.0;
+       return (sample / (double) _sample_rate) / 60.0;
 }
 
 void
 }
 
 void
@@ -765,8 +817,8 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
 
        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<TempoSection*> (*i) != 0) {
 
        for (i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<TempoSection*> (*i) != 0) {
-                       if (tempo.frame() == (*i)->frame()) {
-                               if ((*i)->movable()) {
+                       if (tempo.sample() == (*i)->sample()) {
+                               if (!(*i)->initial()) {
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
@@ -806,7 +858,7 @@ TempoMap::remove_meter_locked (const MeterSection& meter)
                for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        TempoSection* t = 0;
                        if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
                for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        TempoSection* t = 0;
                        if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                               if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
+                               if (t->locked_to_meter() && meter.sample() == (*i)->sample()) {
                                        delete (*i);
                                        _metrics.erase (i);
                                        break;
                                        delete (*i);
                                        _metrics.erase (i);
                                        break;
@@ -817,8 +869,8 @@ TempoMap::remove_meter_locked (const MeterSection& meter)
 
        for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                if (dynamic_cast<MeterSection*> (*i) != 0) {
 
        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 (meter.sample() == (*i)->sample()) {
+                               if (!(*i)->initial()) {
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
                                        delete (*i);
                                        _metrics.erase (i);
                                        return true;
@@ -870,9 +922,9 @@ TempoMap::do_insert (MetricSection* section)
 
                        /* Tempo sections */
                        bool const ipm = insert_tempo->position_lock_style() == MusicTime;
 
                        /* Tempo sections */
                        bool const ipm = insert_tempo->position_lock_style() == MusicTime;
-                       if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
+                       if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->sample() == insert_tempo->sample())) {
 
 
-                               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
@@ -881,10 +933,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);
@@ -899,9 +947,9 @@ TempoMap::do_insert (MetricSection* section)
 
                        bool const ipm = insert_meter->position_lock_style() == MusicTime;
 
 
                        bool const ipm = insert_meter->position_lock_style() == MusicTime;
 
-                       if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
+                       if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->sample() == insert_meter->sample())) {
 
 
-                               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
@@ -931,15 +979,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())) {
+                                       if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->sample() > insert_meter->sample())) {
+                                               break;
+                                       }
+                               } else {
+                                       if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->sample() == insert_meter->sample())) {
                                                break;
                                        }
                                                break;
                                        }
+
+                                       prev_t = dynamic_cast<TempoSection*> (*i);
                                }
                        }
                } else if (insert_tempo) {
                                }
                        }
                } else if (insert_tempo) {
@@ -948,7 +1005,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->sample() > insert_tempo->sample())
+                                           || (lm && tempo->pulse() > insert_tempo->pulse())) {
                                                break;
                                        }
                                }
                                                break;
                                        }
                                }
@@ -956,19 +1015,26 @@ 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*
        }
 }
 /* user supplies the exact pulse if pls == MusicTime */
 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 samplepos_t sample, 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;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
        TempoSection* ts = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
-       }
+               /* here we default to not clamped for a new tempo section. preference? */
+               ts = add_tempo_locked (tempo, pulse, minute_at_sample (sample), pls, true, false, false);
 
 
+               recompute_map (_metrics);
+       }
 
        PropertyChanged (PropertyChange ());
 
 
        PropertyChanged (PropertyChange ());
 
@@ -976,27 +1042,49 @@ 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 samplepos_t sample, 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, minute_at_frame (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_sample (sample), pls, true, locked_to_meter, ts_clamped);
+                               /* enforce clampedness of next tempo section */
+                               TempoSection* next_t = next_tempo_section_locked (_metrics, new_ts);
+                               if (next_t && next_t->clamped()) {
+                                       next_t->set_note_types_per_minute (new_ts->end_note_types_per_minute());
+                               }
+                       }
+
                } else {
                } else {
-                       first.set_type (type);
                        first.set_pulse (0.0);
                        first.set_pulse (0.0);
-                       first.set_minute (minute_at_frame (frame));
+                       first.set_minute (minute_at_sample (sample));
                        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 ());
        }
 
        PropertyChanged (PropertyChange ());
@@ -1004,43 +1092,53 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
 
 TempoSection*
 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
 
 TempoSection*
 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
-                           , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
+                           , PositionLockStyle pls, bool recompute, bool locked_to_meter, bool clamped)
 {
 {
-       TempoSection* t = new TempoSection (pulse, minute, tempo.beats_per_minute(), tempo.note_type(), type, pls, _frame_rate);
+       TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _sample_rate);
        t->set_locked_to_meter (locked_to_meter);
        t->set_locked_to_meter (locked_to_meter);
-       bool solved = false;
+       t->set_clamped (clamped);
 
        do_insert (t);
 
 
        do_insert (t);
 
+       TempoSection* prev_tempo = 0;
+       for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+               TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
+               if (this_t) {
+                       if (this_t == t) {
+                               if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
+                                       prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
+                               }
+                               break;
+                       }
+                       prev_tempo = this_t;
+               }
+       }
+
        if (recompute) {
                if (pls == AudioTime) {
        if (recompute) {
                if (pls == AudioTime) {
-                       solved = solve_map_minute (_metrics, t, t->minute());
+                       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, PositionLockStyle pls)
+TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
 {
        MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 {
        MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               m = add_meter_locked (meter, beat, where, pls, true);
+               m = add_meter_locked (meter, where, sample, pls, true);
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
        }
 
 
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
-               dump (_metrics, std::cerr);
+               dump (std::cerr);
        }
 #endif
 
        }
 #endif
 
@@ -1049,15 +1147,14 @@ 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, PositionLockStyle pls)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 {
        {
                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);
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, beat, where, pls, true);
+                       add_meter_locked (meter, where, sample, pls, true);
                } else {
                        MeterSection& first (first_meter());
                        TempoSection& first_t (first_tempo());
                } else {
                        MeterSection& first (first_meter());
                        TempoSection& first_t (first_tempo());
@@ -1065,10 +1162,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_minute (minute_at_frame (frame));
+                       first.set_minute (minute_at_sample (sample));
                        pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
                        first.set_beat (beat);
                        first_t.set_minute (first.minute());
                        pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
                        first.set_beat (beat);
                        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);
@@ -1079,24 +1177,26 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 }
 
 MeterSection*
 }
 
 MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, samplepos_t sample, PositionLockStyle pls, bool recompute)
 {
 {
-       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 time_minutes = minute_at_pulse_locked (_metrics, pulse);
-       TempoSection* mlt = 0;
+       double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
+       const MeterSection& prev_m = meter_section_at_minute_locked  (_metrics, minute_at_bbt - minute_at_sample (1));
+       double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
+       /* the natural time of the BBT position */
+       double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
 
        if (pls == AudioTime) {
 
        if (pls == AudioTime) {
-               /* add meter-locked tempo */
-               mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse,  time_minutes, TempoSection::Ramp, AudioTime, true, true);
+               /* add meter-locked tempo at the natural time in the current map (sample may differ). */
+               Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
+               TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true, false);
 
                if (!mlt) {
                        return 0;
                }
 
                if (!mlt) {
                        return 0;
                }
-
        }
        }
+       /* still using natural time for the position, ignoring lock style. */
+       MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat_at_bbt_locked (_metrics, bbt), bbt, meter.divisions_per_bar(), meter.note_divisor(), pls, _sample_rate);
 
 
-       MeterSection* new_meter = new MeterSection (pulse, time_minutes, 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);
@@ -1104,9 +1204,16 @@ 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_minute (_metrics, new_meter, time_minutes);
+                       /* now set the audio locked meter's position to sample */
+                       solved = solve_map_minute (_metrics, new_meter, minute_at_sample (sample));
+                       /* we failed, most likely due to some impossible sample requirement wrt audio-locked tempi.
+                          fudge sample so that the meter ends up at its BBT position instead.
+                       */
+                       if (!solved) {
+                               solved = solve_map_minute (_metrics, new_meter, minute_at_sample (prev_m.sample() + 1));
+                       }
                } else {
                } else {
-                       solved = solve_map_bbt (_metrics, new_meter, where);
+                       solved = solve_map_bbt (_metrics, new_meter, bbt);
                        /* required due to resetting the pulse of meter-locked tempi above.
                           Arguably  solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
                           but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
                        /* required due to resetting the pulse of meter-locked tempi above.
                           Arguably  solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
                           but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
@@ -1127,9 +1234,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) {
@@ -1149,9 +1256,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 (samplepos_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;
@@ -1162,7 +1269,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d
 
        for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
 
 
        for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
 
-               if ((*i)->frame() > where) {
+               if ((*i)->sample() > where) {
                        break;
                }
 
                        break;
                }
 
@@ -1246,7 +1353,7 @@ TempoMap::first_tempo () const
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                return *t;
                        }
                }
                                return *t;
                        }
                }
@@ -1267,7 +1374,7 @@ TempoMap::first_tempo ()
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                return *t;
                        }
                }
                                return *t;
                        }
                }
@@ -1290,36 +1397,38 @@ 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;
                                        continue;
                                }
                        }
                                if (!prev_t) {
                                        t->set_pulse (0.0);
                                        prev_t = t;
                                        continue;
                                }
                        }
+
                        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_minute (t->beats_per_minute(), t->minute()));
+                                       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->beats_per_minute(), t->minute()));
+                                               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->beats_per_minute(), t->pulse()));
-                                       t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
+                                       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.
    the current approach is to use a meter's bbt time as its base position unit.
    an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
 }
 
 /* tempos must be positioned correctly.
    the current approach is to use a meter's bbt time as its base position unit.
    an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
-   while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
+   while a music-locked meter requires recomputations of sample pulse and beat (but not bbt)
 */
 void
 TempoMap::recompute_meters (Metrics& metrics)
 */
 void
 TempoMap::recompute_meters (Metrics& metrics)
@@ -1338,7 +1447,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->sample() == meter->sample()) {
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
@@ -1346,14 +1455,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());
                                        }
@@ -1397,7 +1508,7 @@ TempoMap::recompute_meters (Metrics& metrics)
 }
 
 void
 }
 
 void
-TempoMap::recompute_map (Metrics& metrics, framepos_t end)
+TempoMap::recompute_map (Metrics& metrics, samplepos_t end)
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
@@ -1414,11 +1525,15 @@ TempoMap::recompute_map (Metrics& metrics, framepos_t end)
 }
 
 TempoMetric
 }
 
 TempoMetric
-TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
+TempoMap::metric_at (samplepos_t sample, Metrics::const_iterator* last) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
 
+       if (last) {
+               *last = ++_metrics.begin();
+       }
+
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
           TempoMap construction.
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
           TempoMap construction.
@@ -1428,7 +1543,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 
        for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
 
        for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
 
-               if ((*i)->frame() > frame) {
+               if ((*i)->sample() > sample) {
                        break;
                }
 
                        break;
                }
 
@@ -1473,21 +1588,21 @@ TempoMap::metric_at (BBT_Time bbt) const
        return m;
 }
 
        return m;
 }
 
-/** 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.
+/** Returns the BBT (meter-based) beat corresponding to the supplied sample, possibly returning a negative value.
+ * @param sample The session sample position.
+ * @return The beat duration according to the tempo map at the supplied sample.
  *
  *
- * If the supplied frame lies before the first meter, the returned beat duration will be negative.
+ * If the supplied sample 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).
  *
  * This function uses both tempo and meter.
  */
 double
  * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
  *
  * This function uses both tempo and meter.
  */
 double
-TempoMap::beat_at_frame (const framecnt_t& frame) const
+TempoMap::beat_at_sample (const samplecnt_t sample) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return beat_at_minute_locked (_metrics, minute_at_frame (frame));
+       return beat_at_minute_locked (_metrics, minute_at_sample (sample));
 }
 
 /* This function uses both tempo and meter.*/
 }
 
 /* This function uses both tempo and meter.*/
@@ -1518,18 +1633,18 @@ TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) c
        return beat;
 }
 
        return beat;
 }
 
-/** Returns the frame corresponding to the supplied BBT (meter-based) beat.
+/** Returns the sample corresponding to the supplied BBT (meter-based) beat.
  * @param beat The 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.
+ * @return The sample duration according to the tempo map at the supplied BBT (meter-based) beat.
  *
  * This function uses both tempo and meter.
  */
  *
  * This function uses both tempo and meter.
  */
-framepos_t
-TempoMap::frame_at_beat (const double& beat) const
+samplepos_t
+TempoMap::sample_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return frame_at_minute (minute_at_beat_locked (_metrics, beat));
+       return sample_at_minute (minute_at_beat_locked (_metrics, beat));
 }
 
 /* meter & tempo section based */
 }
 
 /* meter & tempo section based */
@@ -1550,12 +1665,18 @@ TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) con
                        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;
                        }
@@ -1563,21 +1684,22 @@ TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) con
                }
 
        }
                }
 
        }
+       assert (prev_t);
 
        return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
 }
 
 
        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.
+/** Returns a Tempo corresponding to the supplied sample position.
+ * @param sample The audio sample.
+ * @return a Tempo according to the tempo map at the supplied sample.
  *
  */
 Tempo
  *
  */
 Tempo
-TempoMap::tempo_at_frame (const framepos_t& frame) const
+TempoMap::tempo_at_sample (const samplepos_t sample) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
+       return tempo_at_minute_locked (_metrics, minute_at_sample (sample));
 }
 
 Tempo
 }
 
 Tempo
@@ -1594,39 +1716,34 @@ TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute)
                                continue;
                        }
                        if ((prev_t) && t->minute() > minute) {
                                continue;
                        }
                        if ((prev_t) && t->minute() > minute) {
-                               /* t is the section past frame */
-                               const double ret_bpm = prev_t->tempo_at_minute (minute);
-                               const Tempo ret_tempo (ret_bpm, prev_t->note_type());
-                               return ret_tempo;
+                               /* t is the section past sample */
+                               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
- *  the frame of the last tempo section (search exhausted)
+/** returns the sample at which the supplied tempo occurs, or
+ *  the sample of the last tempo section (search exhausted)
  *  only the position of the first occurence will be returned
  *  (extend me)
 */
  *  only the position of the first occurence will be returned
  *  (extend me)
 */
-framepos_t
-TempoMap::frame_at_tempo (const Tempo& tempo) const
+samplepos_t
+TempoMap::sample_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
+       return sample_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;
 }
 
 double
 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
 {
        TempoSection* prev_t = 0;
-       const double tempo_bpm = tempo.beats_per_minute();
+       const double tempo_bpm = tempo.note_types_per_minute();
 
        Metrics::const_iterator i;
 
 
        Metrics::const_iterator i;
 
@@ -1639,17 +1756,20 @@ TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) co
                                continue;
                        }
 
                                continue;
                        }
 
-                       const double t_bpm = t->beats_per_minute();
 
 
-                       if (t_bpm == tempo_bpm) {
+
+                       if (t->note_types_per_minute() == tempo_bpm) {
                                return t->minute();
                        }
 
                        if (prev_t) {
                                return t->minute();
                        }
 
                        if (prev_t) {
-                               const double prev_t_bpm = prev_t->beats_per_minute();
+                               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)) {
 
 
-                               if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
-                                       return prev_t->minute_at_tempo (tempo_bpm, prev_t->pulse());
+                                       return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
                                }
                        }
                        prev_t = t;
                                }
                        }
                        prev_t = t;
@@ -1673,26 +1793,21 @@ TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) co
                                continue;
                        }
                        if ((prev_t) && t->pulse() > pulse) {
                                continue;
                        }
                        if ((prev_t) && t->pulse() > pulse) {
-                               /* t is the section past frame */
-                               const double ret_bpm = prev_t->tempo_at_pulse (pulse);
-                               const Tempo ret_tempo (ret_bpm, prev_t->note_type());
-                               return ret_tempo;
+                               /* t is the section past sample */
+                               return prev_t->tempo_at_pulse (pulse);
                        }
                        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());
 }
 
 double
 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
 {
        TempoSection* prev_t = 0;
 }
 
 double
 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
 {
        TempoSection* prev_t = 0;
-       const double tempo_bpm = tempo.beats_per_minute();
+       const double tempo_bpm = tempo.note_types_per_minute();
 
        Metrics::const_iterator i;
 
 
        Metrics::const_iterator i;
 
@@ -1705,17 +1820,17 @@ TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con
                                continue;
                        }
 
                                continue;
                        }
 
-                       const double t_bpm = t->beats_per_minute();
+                       const double t_bpm = t->note_types_per_minute();
 
                        if (t_bpm == tempo_bpm) {
                                return t->pulse();
                        }
 
                        if (prev_t) {
 
                        if (t_bpm == tempo_bpm) {
                                return t->pulse();
                        }
 
                        if (prev_t) {
-                               const double prev_t_bpm = prev_t->beats_per_minute();
+                               const double prev_t_bpm = prev_t->note_types_per_minute();
 
                                if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
 
                                if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
-                                       return prev_t->pulse_at_tempo (tempo_bpm, prev_t->minute());
+                                       return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
                                }
                        }
                        prev_t = t;
                                }
                        }
                        prev_t = t;
@@ -1730,13 +1845,11 @@ TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con
  * @return the Tempo at the supplied quarter-note.
  */
 Tempo
  * @return the Tempo at the supplied quarter-note.
  */
 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);
 
 
-       return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()), prev_t->note_type());
+       return tempo_at_pulse_locked (_metrics, qn / 4.0);
 }
 
 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
 }
 
 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
@@ -1745,12 +1858,11 @@ TempoMap::tempo_at_beat (const double& beat) const
  * is equal to that of the Tempo. currently ignores note_type.
  */
 double
  * is equal to that of the Tempo. currently ignores note_type.
  */
 double
-TempoMap::beat_at_tempo (const Tempo& tempo) const
+TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       const double pulse = pulse_at_tempo_locked (_metrics, tempo);
 
 
-       return beat_at_pulse_locked (_metrics, pulse);
+       return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
 }
 
 /** Returns the whole-note pulse corresponding to the supplied  BBT (meter-based) beat.
 }
 
 /** Returns the whole-note pulse corresponding to the supplied  BBT (meter-based) beat.
@@ -1793,6 +1905,7 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con
                        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;
@@ -1813,7 +1926,7 @@ TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute)
                                continue;
                        }
                        if (prev_t && t->minute() > minute) {
                                continue;
                        }
                        if (prev_t && t->minute() > minute) {
-                               /*the previous ts is the one containing the frame */
+                               /*the previous ts is the one containing the sample */
                                const double ret = prev_t->pulse_at_minute (minute);
                                /* audio locked section in new meter*/
                                if (t->pulse() < ret) {
                                const double ret = prev_t->pulse_at_minute (minute);
                                /* audio locked section in new meter*/
                                if (t->pulse() < ret) {
@@ -1826,7 +1939,7 @@ TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute)
        }
 
        /* treated as constant for this ts */
        }
 
        /* treated as constant for this ts */
-       const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->beats_per_minute()) / prev_t->note_type();
+       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();
 }
@@ -1855,7 +1968,7 @@ TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) c
                }
        }
        /* must be treated as constant, irrespective of _type */
                }
        }
        /* must be treated as constant, irrespective of _type */
-       double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->beats_per_minute();
+       double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
 
        return dtime + prev_t->minute();
 }
 
        return dtime + prev_t->minute();
 }
@@ -1939,6 +2052,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());
@@ -1978,10 +2092,18 @@ TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
  * while the input uses meter, the output does not.
  */
 double
  * while the input uses meter, the output does not.
  */
 double
-TempoMap::quarter_note_at_bbt_rt (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) * 4.0;
+}
+
+double
+TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
        }
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
        }
@@ -2020,14 +2142,29 @@ TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time&
        return ret;
 }
 
        return ret;
 }
 
-/** 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.
+/** 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).
  *
  *
- * 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_quarter_note (const double& qn)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       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
@@ -2053,6 +2190,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);
@@ -2082,37 +2221,44 @@ 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 .
+/** Returns the BBT time corresponding to the supplied sample position.
+ * @param sample the position in audio samples.
+ * @return the BBT time at the sample position .
  *
  */
 BBT_Time
  *
  */
 BBT_Time
-TempoMap::bbt_at_frame (framepos_t frame)
+TempoMap::bbt_at_sample (samplepos_t sample)
 {
 {
-       if (frame < 0) {
+       if (sample < 0) {
                BBT_Time bbt;
                bbt.bars = 1;
                bbt.beats = 1;
                bbt.ticks = 0;
                BBT_Time bbt;
                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 sample %1\n"), sample) << endmsg;
+#endif
                return bbt;
        }
                return bbt;
        }
+
+       const double minute =  minute_at_sample (sample);
+
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
+       return bbt_at_minute_locked (_metrics, minute);
 }
 
 BBT_Time
 }
 
 BBT_Time
-TempoMap::bbt_at_frame_rt (framepos_t frame)
+TempoMap::bbt_at_sample_rt (samplepos_t sample)
 {
 {
+       const double minute =  minute_at_sample (sample);
+
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
        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");
+               throw std::logic_error ("TempoMap::bbt_at_sample_rt() could not lock tempo map");
        }
 
        }
 
-       return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
+       return bbt_at_minute_locked (_metrics, minute);
 }
 
 Timecode::BBT_Time
 }
 
 Timecode::BBT_Time
@@ -2145,7 +2291,7 @@ TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) co
 
        double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - 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 sample before first meter */
        if (minute < prev_m->minute()) {
                beat = 0.0;
        }
        if (minute < prev_m->minute()) {
                beat = 0.0;
        }
@@ -2185,25 +2331,32 @@ TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) co
        return ret;
 }
 
        return ret;
 }
 
-/** Returns the frame position corresponding to the supplied BBT time.
+/** Returns the sample position corresponding to the supplied BBT time.
  * @param bbt the position in BBT time.
  * @param bbt the position in BBT time.
- * @return the frame position at bbt.
+ * @return the sample position at bbt.
  *
  */
  *
  */
-framepos_t
-TempoMap::frame_at_bbt (const BBT_Time& bbt)
+samplepos_t
+TempoMap::sample_at_bbt (const BBT_Time& bbt)
 {
        if (bbt.bars < 1) {
 {
        if (bbt.bars < 1) {
-               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
+#ifndef NDEBUG
+               warning << string_compose (_("tempo map asked for sample 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_minute (minute_at_bbt_locked (_metrics, bbt));
+       double minute;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               minute = minute_at_bbt_locked (_metrics, bbt);
+       }
+
+       return sample_at_minute (minute);
 }
 
 /* meter & tempo section based */
 }
 
 /* meter & tempo section based */
@@ -2217,144 +2370,166 @@ TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) con
 }
 
 /**
 }
 
 /**
- * Returns the quarter-note beat position corresponding to the supplied frame.
+ * Returns the quarter-note beat position corresponding to the supplied sample.
  *
  *
- * @param frame the position in frames.
- * @return The quarter-note position of the supplied frame. Ignores meter.
+ * @param sample the position in samples.
+ * @return The quarter-note position of the supplied sample. Ignores meter.
  *
 */
 double
  *
 */
 double
-TempoMap::quarter_note_at_frame (const framepos_t frame)
+TempoMap::quarter_note_at_sample (const samplepos_t sample) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       const double minute =  minute_at_sample (sample);
 
 
-       const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-       return ret;
+       return pulse_at_minute_locked (_metrics, minute) * 4.0;
 }
 
 double
 }
 
 double
-TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
+TempoMap::quarter_note_at_sample_rt (const samplepos_t sample) const
 {
 {
-       const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
+       const double minute =  minute_at_sample (sample);
 
 
-       return ret;
-}
-
-double
-TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
-{
        Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
 
        if (!lm.locked()) {
        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");
+               throw std::logic_error ("TempoMap::quarter_note_at_sample_rt() could not lock tempo map");
        }
 
        }
 
-       const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (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.
+ * Returns the sample position corresponding to the supplied quarter-note beat.
  *
  * @param quarter_note the quarter-note position.
  *
  * @param quarter_note the quarter-note position.
- * @return the frame position of the supplied quarter-note. Ignores meter.
+ * @return the sample position of the supplied quarter-note. Ignores meter.
  *
  *
 */
  *
  *
 */
-framepos_t
-TempoMap::frame_at_quarter_note (const double quarter_note)
+samplepos_t
+TempoMap::sample_at_quarter_note (const double quarter_note) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
-
-       return ret;
-}
+       double minute;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
-double
-TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
-{
-       const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
+               minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
+       }
 
 
-       return ret;
+       return sample_at_minute (minute);
 }
 
 /** 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.
  *
 }
 
 /** 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.
+ * a quarter-note may be compared with and assigned to Temporal::Beats.
  *
  */
 double
  *
  */
 double
-TempoMap::quarter_note_at_beat (const double beat)
+TempoMap::quarter_note_at_beat (const double beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double ret = quarter_note_at_beat_locked (_metrics, beat);
-
-       return ret;
-}
-
-double
-TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
-{
-       const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
-
-       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.
  *
 }
 
 /** 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.
+ * a quarter-note is the musical unit of Temporal::Beats.
  *
  */
 double
  *
  */
 double
-TempoMap::beat_at_quarter_note (const double quarter_note)
+TempoMap::beat_at_quarter_note (const double quarter_note) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
-
-       return ret;
-}
-
-double
-TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
-{
-
-       return beat_at_pulse_locked (metrics, quarter_note / 4.0);
+       return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
 }
 
 }
 
-/** Returns the duration in frames between two supplied quarter-note beat positions.
+/** Returns the duration in samples 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.
  * @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.
+ * @return the sample distance ober the quarter-note beats duration.
  *
  * use this rather than e.g.
  *
  * 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,
+ * sample_at-quarter_note (end_beats) - sample_at_quarter_note (start_beats).
+ * samples_between_quarter_notes() doesn't round to audio samples as an intermediate step,
  *
  */
  *
  */
-framecnt_t
-TempoMap::frames_between_quarter_notes (const double start, const double end)
+samplecnt_t
+TempoMap::samples_between_quarter_notes (const double start, const double end) const
 {
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       double minutes;
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (lock);
+               minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
+       }
 
 
-       return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
+       return sample_at_minute (minutes);
 }
 
 double
 }
 
 double
-TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end)
+TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
 {
 
        return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
 }
 
 {
 
        return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
 }
 
+double
+TempoMap::quarter_notes_between_samples (const samplecnt_t start, const samplecnt_t end) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return quarter_notes_between_samples_locked (_metrics, start, end);
+}
+
+double
+TempoMap::quarter_notes_between_samples_locked (const Metrics& metrics, const samplecnt_t start, const samplecnt_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->sample() > start) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+       }
+       assert (prev_t);
+       const double start_qn = prev_t->pulse_at_sample (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->sample() > end) {
+                               break;
+                       }
+                       prev_t = t;
+               }
+       }
+       const double end_qn = prev_t->pulse_at_sample (end);
+
+       return (end_qn - start_qn) * 4.0;
+}
+
 bool
 TempoMap::check_solved (const Metrics& metrics) const
 {
 bool
 TempoMap::check_solved (const Metrics& metrics) const
 {
@@ -2375,8 +2550,8 @@ TempoMap::check_solved (const Metrics& metrics) const
                                        return false;
                                }
 
                                        return false;
                                }
 
-                               /* precision check ensures tempo and frames align.*/
-                               if (t->frame() != frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))) {
+                               /* precision check ensures tempo and samples align.*/
+                               if (t->sample() != sample_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;
                                        }
@@ -2385,8 +2560,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;
                                }
                        }
@@ -2396,11 +2571,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_minute_locked (metrics, minute_at_frame (m->frame() - 1));
-                               const double nascent_m_minute = t->minute_at_pulse (m->pulse());
+                               const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_sample (m->sample() - 1));
+                               const samplepos_t nascent_m_sample = sample_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.
                                */
-                               if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
+                               if (t && (nascent_m_sample > m->sample() || nascent_m_sample < 0)) {
                                        return false;
                                }
                        }
                                        return false;
                                }
                        }
@@ -2414,23 +2589,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 samplepos_t sample)
 {
        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->sample() < sample) {
+                                       t->set_active (false);
+                                       t->set_pulse (-1.0);
+                               } else if (t->sample() > sample) {
+                                       t->set_active (true);
+                               } else if (t->sample() == sample) {
+                                       return false;
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -2443,19 +2618,20 @@ TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const dou
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
        double first_m_minute = 0.0;
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 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()) {
+                       if (m->initial()) {
                                first_m_minute = m->minute();
                                break;
                        }
                }
        }
                                first_m_minute = m->minute();
                                break;
                        }
                }
        }
-       if (section->movable() && minute <= first_m_minute) {
+       if (!section->initial() && minute <= first_m_minute) {
                return false;
        }
 
                return false;
        }
 
@@ -2470,21 +2646,36 @@ TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const dou
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
+
                        if (prev_t) {
                        if (prev_t) {
+
                                if (t == section) {
                                if (t == section) {
+                                       continue;
+                               }
+
+                               if (t->sample() == sample_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->beats_per_minute(), t->pulse()));
-                                       t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
+                                       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_minute (t->beats_per_minute(), t->minute()));
+                                       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->beats_per_minute(), t->minute()));
+                                               t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -2492,23 +2683,6 @@ TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const dou
                }
        }
 
                }
        }
 
-       if (section_prev) {
-               section_prev->set_c_func (section_prev->compute_c_func_minute (section->beats_per_minute(), minute));
-               if (!section->locked_to_meter()) {
-                       section->set_pulse (section_prev->pulse_at_tempo (section->beats_per_minute(), minute));
-               }
-       }
-
-#if (0)
-       recompute_tempi (imaginary);
-
-       if (check_solved (imaginary)) {
-               return true;
-       } else {
-               dunp (imaginary, std::cout);
-       }
-#endif
-
        MetricSectionFrameSorter fcmp;
        imaginary.sort (fcmp);
 
        MetricSectionFrameSorter fcmp;
        imaginary.sort (fcmp);
 
@@ -2536,7 +2710,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;
@@ -2546,13 +2720,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->beats_per_minute(), t->pulse()));
-                                       t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
+                                       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_minute (t->beats_per_minute(), t->minute()));
+                                       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->beats_per_minute(), t->minute()));
+                                               t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -2561,19 +2736,9 @@ 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->beats_per_minute(), pulse));
-               section->set_minute (section_prev->minute_at_tempo (section->beats_per_minute(), pulse));
-       }
-
-#if (0)
-       recompute_tempi (imaginary);
-
-       if (check_solved (imaginary)) {
-               return true;
-       } else {
-               dunp (imaginary, std::cout);
+               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));
        }
        }
-#endif
 
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
 
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
@@ -2599,15 +2764,15 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
 bool
 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
 {
 bool
 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 */
+       /* 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);
        const MeterSection* other =  &meter_section_at_minute_locked (imaginary, minute);
-       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= 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, section->frame_at_minute (minute))) {
+               if (!set_active_tempi (imaginary, sample_at_minute (minute))) {
                        return false;
                }
        }
                        return false;
                }
        }
@@ -2618,7 +2783,7 @@ TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const dou
                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->minute() == section->minute()) {
+                       if (t->locked_to_meter() && t->sample() == section->sample()) {
                                meter_locked_tempo = t;
                                break;
                        }
                                meter_locked_tempo = t;
                                break;
                        }
@@ -2639,7 +2804,7 @@ TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const dou
                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()) {
+                               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()) {
                                                /* set the section pulse according to its musical position,
                                        const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
                                        if (beats + prev_m->beat() < section->beat()) {
                                                /* set the section pulse according to its musical position,
@@ -2673,7 +2838,7 @@ TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const dou
                                                }
                                        } 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.
                                                */
                                                MeterSection* meter_copy = const_cast<MeterSection*>
                                                   XX this check could possibly go. its never actually happened here.
                                                */
                                                MeterSection* meter_copy = const_cast<MeterSection*>
@@ -2701,7 +2866,7 @@ TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const dou
                                                }
                                        }
                                } else {
                                                }
                                        }
                                } else {
-                                       /* not movable (first meter atm) */
+                                       /* initial (first meter atm) */
 
                                        tempo_copy->set_minute (minute);
                                        tempo_copy->set_pulse (0.0);
 
                                        tempo_copy->set_minute (minute);
                                        tempo_copy->set_pulse (0.0);
@@ -2766,11 +2931,17 @@ 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);
                                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);
@@ -2779,7 +2950,6 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                                section->set_pulse (pulse);
                                section->set_minute (minute_at_pulse_locked (imaginary, pulse));
                                prev_m = section;
                                section->set_pulse (pulse);
                                section->set_minute (minute_at_pulse_locked (imaginary, pulse));
                                prev_m = section;
-                               continue;
                        }
 
                        if (m->position_lock_style() == AudioTime) {
                        }
 
                        if (m->position_lock_style() == AudioTime) {
@@ -2789,7 +2959,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->sample() == m->sample()) {
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
                                                        meter_locked_tempo = t;
                                                        break;
                                                }
@@ -2801,14 +2971,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());
                                        }
@@ -2864,15 +3041,13 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
  *  to section's equivalent in copy.
  */
 TempoSection*
  *  to section's equivalent in copy.
  */
 TempoSection*
-TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
+TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
 {
        TempoSection* ret = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 {
        TempoSection* ret = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-               MeterSection* m;
                if ((*i)->is_tempo()) {
                if ((*i)->is_tempo()) {
-                       t = static_cast<TempoSection*> (*i);
+                       TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
                        if (t == section) {
                                ret = new TempoSection (*t);
                                copy.push_back (ret);
                        if (t == section) {
                                ret = new TempoSection (*t);
                                copy.push_back (ret);
@@ -2881,9 +3056,8 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe
 
                        TempoSection* cp = new TempoSection (*t);
                        copy.push_back (cp);
 
                        TempoSection* cp = new TempoSection (*t);
                        copy.push_back (cp);
-               }
-               if (!(*i)->is_tempo()) {
-                       m = static_cast<MeterSection *> (*i);
+               } else {
+                       MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
                        MeterSection* cp = new MeterSection (*m);
                        copy.push_back (cp);
                }
                        MeterSection* cp = new MeterSection (*m);
                        copy.push_back (cp);
                }
@@ -2893,21 +3067,17 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe
 }
 
 MeterSection*
 }
 
 MeterSection*
-TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
+TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
 {
        MeterSection* ret = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 {
        MeterSection* ret = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-               MeterSection* m;
                if ((*i)->is_tempo()) {
                if ((*i)->is_tempo()) {
-                       t = static_cast<TempoSection*> (*i);
+                       TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
                        TempoSection* cp = new TempoSection (*t);
                        copy.push_back (cp);
                        TempoSection* cp = new TempoSection (*t);
                        copy.push_back (cp);
-               }
-
-               if (!(*i)->is_tempo()) {
-                       m = static_cast<MeterSection *> (*i);
+               } else {
+                       MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
                        if (m == section) {
                                ret = new MeterSection (*m);
                                copy.push_back (ret);
                        if (m == section) {
                                ret = new MeterSection (*m);
                                copy.push_back (ret);
@@ -2954,17 +3124,17 @@ 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,
+* This is for a gui that needs to know the pulse or sample 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 time  where the altered tempo will fall
 * taking any possible reordering as a consequence of this into account.
 * @param section - the section to be altered
 * @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.
+* @return returns - the position in pulses and samples (as a pair) where the new tempo section will lie.
 */
 */
-pair<double, framepos_t>
+pair<double, samplepos_t>
 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 {
        Metrics future_map;
 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 {
        Metrics future_map;
-       pair<double, framepos_t> ret = make_pair (0.0, 0);
+       pair<double, samplepos_t> ret = make_pair (0.0, 0);
 
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
@@ -2972,12 +3142,16 @@ 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();
        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();
+               ret.second = tempo_copy->sample();
        } else {
                ret.first = section->pulse();
        } else {
                ret.first = section->pulse();
-               ret.second = section->frame();
+               ret.second = section->sample();
        }
 
        Metrics::const_iterator d = future_map.begin();
        }
 
        Metrics::const_iterator d = future_map.begin();
@@ -2990,35 +3164,35 @@ TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 
 /** moves a TempoSection to a specified position.
  * @param ts - the section to be moved
 
 /** 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 sample - the new position in samples for the tempo
  * @param sub_num - the snap division to use if using musical time.
  *
  * @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
+ * if sub_num is non-zero, the sample position is used to calculate an exact
  * musical position.
  * sub_num   | effect
  * -1        | snap to bars (meter-based)
  * musical position.
  * sub_num   | effect
  * -1        | snap to bars (meter-based)
- *  0        | no snap - use audio frame for musical position
+ *  0        | no snap - use audio sample 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.
  *  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.
+ * if sub_num is zero, the musical position will be taken from the supplied sample.
  */
 void
  */
 void
-TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
+TempoMap::gui_set_tempo_position (TempoSection* ts, const samplepos_t sample, const int& sub_num)
 {
        Metrics future_map;
 
        if (ts->position_lock_style() == MusicTime) {
                {
 {
        Metrics future_map;
 
        if (ts->position_lock_style() == MusicTime) {
                {
-                       /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
+                       /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied sample. */
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
 
                        tempo_copy->set_position_lock_style (AudioTime);
 
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
 
                        tempo_copy->set_position_lock_style (AudioTime);
 
-                       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);
+                       if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
+                               const double beat = exact_beat_at_sample_locked (future_map, sample, sub_num);
                                const double pulse = pulse_at_beat_locked (future_map, beat);
 
                                if (solve_map_pulse (future_map, tempo_copy, pulse)) {
                                const double pulse = pulse_at_beat_locked (future_map, beat);
 
                                if (solve_map_pulse (future_map, tempo_copy, pulse)) {
@@ -3034,30 +3208,25 @@ 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_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).
-                                        * 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.
-                                        */
-                                       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);
 
 
-                                               recompute_meters (_metrics);
-                                       }
-                               } else {
-                                       solve_map_minute (_metrics, ts, 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 sample 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).
+                                * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
+                                */
+                               const double qn = exact_qn_at_sample_locked (_metrics, sample, sub_num);
+                               tempo_copy->set_position_lock_style (MusicTime);
+                               if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
+                                       ts->set_position_lock_style (MusicTime);
+                                       solve_map_pulse (_metrics, ts, qn / 4.0);
+                                       ts->set_position_lock_style (AudioTime);
+                                       recompute_meters (_metrics);
+                               }
+                       } else {
+                               if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
+                                       solve_map_minute (_metrics, ts, minute_at_sample (sample));
                                        recompute_meters (_metrics);
                                }
                        }
                                        recompute_meters (_metrics);
                                }
                        }
@@ -3070,19 +3239,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
 }
 
 /** moves a MeterSection to a specified position.
  * @param ms - the section to be moved
- * @param frame - the new position in frames for the meter
+ * @param sample - the new position in samples for the meter
  *
  * as a meter cannot snap to anything but bars,
  *
  * as a meter cannot snap to anything but bars,
- * the supplied frame is rounded to the nearest bar, possibly
+ * the supplied sample is rounded to the nearest bar, possibly
  * leaving the meter position unchanged.
  */
 void
  * leaving the meter position unchanged.
  */
 void
-TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
+TempoMap::gui_set_meter_position (MeterSection* ms, const samplepos_t sample)
 {
        Metrics future_map;
 
 {
        Metrics future_map;
 
@@ -3092,8 +3261,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_minute (future_map, copy, minute_at_frame (frame))) {
-                               solve_map_minute (_metrics, ms, minute_at_frame (frame));
+                       if (solve_map_minute (future_map, copy, minute_at_sample (sample))) {
+                               solve_map_minute (_metrics, ms, minute_at_sample (sample));
                                recompute_tempi (_metrics);
                        }
                }
                                recompute_tempi (_metrics);
                        }
                }
@@ -3102,7 +3271,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_minute_locked (_metrics, minute_at_frame (frame));
+                       const double beat = beat_at_minute_locked (_metrics, minute_at_sample (sample));
                        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)) {
@@ -3118,7 +3287,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
                ++d;
        }
 
                ++d;
        }
 
-       MetricPositionChanged (); // Emit Signal
+       MetricPositionChanged (PropertyChange ()); // Emit Signal
 }
 
 bool
 }
 
 bool
@@ -3129,11 +3298,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;
                }
@@ -3145,20 +3343,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)
+TempoMap::gui_stretch_tempo (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample, const double start_qnote, const double end_qnote)
 {
        /*
          Ts (future prev_t)   Tnext
          |                    |
          |     [drag^]        |
          |----------|----------
 {
        /*
          Ts (future prev_t)   Tnext
          |                    |
          |     [drag^]        |
          |----------|----------
-               e_f  qn_beats(frame)
+               e_f  qn_beats(sample)
        */
 
        Metrics future_map;
        */
 
        Metrics future_map;
@@ -3170,199 +3369,453 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                        return;
                }
 
                        return;
                }
 
-               TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
-               TempoSection* prev_to_prev_t = 0;
-               const frameoffset_t fr_off = end_frame - frame;
-
-               if (prev_t && prev_t->pulse() > 0.0) {
-                       prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
-               }
-
-               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 */
-               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;
+               TempoSection* ts_copy = copy_metrics_and_point (_metrics, future_map, ts);
 
 
-               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());
+               if (!ts_copy) {
+                       return;
                }
 
                }
 
-               const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
-
-               const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
-               const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
+               /* minimum allowed measurement distance in samples */
+               samplepos_t const min_dframe = 2;
 
                double new_bpm;
 
                double new_bpm;
+               if (ts_copy->clamped()) {
+                       TempoSection* next_t = next_tempo_section_locked (future_map, ts_copy);
+                       TempoSection* prev_to_ts_copy = previous_tempo_section_locked (future_map, ts_copy);
+                        /* the change in samples is the result of changing the slope of at most 2 previous tempo sections.
+                          constant to constant is straightforward, as the tempo prev to ts_copy has constant slope.
+                        */                     double contribution = 0.0;
+                       if (next_t && prev_to_ts_copy && prev_to_ts_copy->type() == TempoSection::Ramp) {
+                               contribution = (ts_copy->pulse() - prev_to_ts_copy->pulse()) / (double) (next_t->pulse() - prev_to_ts_copy->pulse());
+                       }
+                       samplepos_t const fr_off = end_sample - sample;
+                       sampleoffset_t const ts_copy_sample_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 (sample > prev_to_ts_copy->sample() + min_dframe && (sample + ts_copy_sample_contribution) > prev_to_ts_copy->sample() + min_dframe) {
+                               new_bpm = ts_copy->note_types_per_minute() * ((start_qnote - (prev_to_ts_copy->pulse() * 4.0))
+                                                                            / (end_qnote - (prev_to_ts_copy->pulse() * 4.0)));
                        } 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 = ts_copy->note_types_per_minute();
                        }
                } else {
                        }
                } else {
+                       if (sample > ts_copy->sample() + min_dframe && end_sample > ts_copy->sample() + min_dframe) {
 
 
-                       double frame_ratio = 1.0;
-                       double pulse_ratio = 1.0;
-                       const double pulse_pos = frame;
-
-                       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 = ts_copy->note_types_per_minute() * ((sample - ts_copy->sample())
+                                                                            / (double) (end_sample - ts_copy->sample()));
                        } 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 = ts_copy->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);
+
+               ts_copy->set_note_types_per_minute (new_bpm);
+
+               if (ts_copy->clamped()) {
+                       TempoSection* prev = 0;
+                       if ((prev = previous_tempo_section_locked (future_map, ts_copy)) != 0) {
+                               prev->set_end_note_types_per_minute (ts_copy->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);
+                       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
-}
 
 
-/** 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.
- *                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.
- *
- * 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
+}
+void
+TempoMap::gui_stretch_tempo_end (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample)
+{
+       /*
+         Ts (future prev_t)   Tnext
+         |                    |
+         |     [drag^]        |
+         |----------|----------
+               e_f  qn_beats(sample)
+       */
+
+       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 samples */
+               samplepos_t const min_dframe = 2;
+               double new_bpm;
+
+               if (sample > prev_t->sample() + min_dframe && end_sample > prev_t->sample() + min_dframe) {
+                       new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->sample() - sample)
+                                                                                / (double) (prev_t->sample() - end_sample));
+               } 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 samplepos_t sample, const samplepos_t end_sample)
+{
+       TempoSection* next_t = 0;
+       TempoSection* next_to_next_t = 0;
+       Metrics future_map;
+       bool can_solve = false;
+
+       /* minimum allowed measurement distance in samples */
+       samplepos_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 sampleoffset_t fr_off = end_sample - sample;
+
+               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_sample (tempo_copy->sample() - 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->sample() - prev_to_prev_t->sample()) / (double) (next_t->sample() - prev_to_prev_t->sample());
+               }
+
+               const sampleoffset_t tempo_copy_sample_contribution = fr_off - (prev_contribution * (double) fr_off);
+
+
+               samplepos_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 (sample > tempo_copy->sample() + min_dframe && (sample + tempo_copy_sample_contribution) > tempo_copy->sample() + min_dframe) {
+                       new_bpm = tempo_copy->note_types_per_minute() * ((sample - tempo_copy->sample())
+                                                                                      / (double) (end_sample - tempo_copy->sample()));
+               } 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 (sample > tempo_copy->sample() + min_dframe && end_sample > tempo_copy->sample() + 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_sample_ratio = 1.0;
+                       double copy_sample_ratio = 1.0;
+
+                       if (next_to_next_t) {
+                               next_sample_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute -  old_next_minute);
+
+                               copy_sample_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
+                       }
+
+                       new_next_bpm = next_t->note_types_per_minute() * next_sample_ratio;
+                       new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_sample_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 sample position of the musical position zero */
+samplepos_t
+TempoMap::music_origin ()
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return first_tempo().sample();
+}
+
+/** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
+ * the supplied sample, possibly returning a negative value.
+ *
+ * @param sample  The session sample 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 (equivalent to quarter_note_at_sample()).
+ * @return The beat position of the supplied sample.
+ *
+ * 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 sample 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).
  *
  * 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_sample (const samplepos_t sample, const int32_t sub_num) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return exact_beat_at_frame_locked (_metrics, frame, sub_num);
+       return exact_beat_at_sample_locked (_metrics, sample, 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_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t divisions) const
 {
 {
-       return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
+       return beat_at_pulse_locked (_metrics, exact_qn_at_sample_locked (metrics, sample, divisions) / 4.0);
 }
 
 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
 }
 
 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
- * the supplied frame, possibly returning a negative value.
- * Supplying a frame position with a non-zero sub_num is equivalent to supplying
- * a quarter-note musical position without frame rounding (see below)
+ * the supplied sample, possibly returning a negative value.
  *
  *
- * @param frame  The session frame position.
+ * @param sample  The session sample 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.
  * @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 (equivalent to quarter_note_at_frame()).
- * @return The quarter note position of the supplied frame.
+ *                0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_sample()).
+ * @return The quarter note position of the supplied sample.
+ *
+ * When working to a musical grid, the use of sub_nom indicates that
+ * the sample position should be interpreted musically.
  *
  *
- * If the supplied frame lies before the first meter, the return will be negative,
+ * 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 sample 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).
  *
  * 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_sample (const samplepos_t sample, const int32_t sub_num) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return exact_qn_at_frame_locked (_metrics, frame, sub_num);
+       return exact_qn_at_sample_locked (_metrics, sample, 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_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t sub_num) const
 {
 {
-       double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
+       double qn = pulse_at_minute_locked (metrics, minute_at_sample (sample)) * 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_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
+               qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_sample (sample)) + 0.5))) * 4.0;
        } else if (sub_num == -1) {
                /* snap to  bar */
                Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
        } else if (sub_num == -1) {
                /* snap to  bar */
                Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
@@ -3383,20 +3836,20 @@ 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.
+/** returns the sample duration of the supplied BBT time at a specified sample position in the tempo map.
+ * @param pos the sample position in the tempo map.
  * @param bbt the distance in BBT time from pos to calculate.
  * @param dir the rounding direction..
  * @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
+ * @return the duration in samples between pos and bbt
 */
 */
-framecnt_t
-TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
+samplecnt_t
+TempoMap::bbt_duration_at (samplepos_t pos, const BBT_Time& bbt, int dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
-       const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
-       const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
+       BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_sample (pos));
+
+       const double divisions = meter_section_at_minute_locked (_metrics, minute_at_sample (pos)).divisions_per_bar();
 
        if (dir > 0) {
                pos_bbt.bars += bbt.bars;
 
        if (dir > 0) {
                pos_bbt.bars += bbt.bars;
@@ -3412,53 +3865,69 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
                        pos_bbt.bars += 1;
                        pos_bbt.beats -= divisions;
                }
                        pos_bbt.bars += 1;
                        pos_bbt.beats -= divisions;
                }
+               const samplecnt_t pos_bbt_sample = sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
+
+               return pos_bbt_sample - pos;
 
 
-               return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
        } else {
        } else {
-               pos_bbt.bars -= bbt.bars;
+
+               if (pos_bbt.bars <= bbt.bars) {
+                       pos_bbt.bars = 1;
+               } else {
+                       pos_bbt.bars -= bbt.bars;
+               }
 
                if (pos_bbt.ticks < bbt.ticks) {
 
                if (pos_bbt.ticks < bbt.ticks) {
-                       if (pos_bbt.beats == 1) {
-                               pos_bbt.bars--;
-                               pos_bbt.beats = divisions;
+                       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 {
                        } else {
-                               pos_bbt.beats--;
+                               pos_bbt.beats = 1;
+                               pos_bbt.ticks = 0;
                        }
                        }
-                       pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
                } else {
                        pos_bbt.ticks -= bbt.ticks;
                }
 
                if (pos_bbt.beats <= bbt.beats) {
                } else {
                        pos_bbt.ticks -= bbt.ticks;
                }
 
                if (pos_bbt.beats <= bbt.beats) {
-                       pos_bbt.bars--;
-                       pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
+                       if (pos_bbt.bars > 1) {
+                               pos_bbt.bars--;
+                               pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
+                       } else {
+                               pos_bbt.beats = 1;
+                       }
                } else {
                        pos_bbt.beats -= bbt.beats;
                }
 
                } else {
                        pos_bbt.beats -= bbt.beats;
                }
 
-               return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
+               return pos - sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
        }
 
        return 0;
 }
 
        }
 
        return 0;
 }
 
-framepos_t
-TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
+MusicSample
+TempoMap::round_to_bar (samplepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Bar);
 }
 
 {
        return round_to_type (fr, dir, Bar);
 }
 
-framepos_t
-TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
+MusicSample
+TempoMap::round_to_beat (samplepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Beat);
 }
 
 {
        return round_to_type (fr, dir, Beat);
 }
 
-framepos_t
-TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
+MusicSample
+TempoMap::round_to_quarter_note_subdivision (samplepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
+       uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_sample (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;
 
@@ -3482,105 +3951,14 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        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;
-               }
-       } else if (dir < 0) {
+//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;
+//             }
 
 
-               /* round to previous (or same iff dir == RoundDownMaybe) */
-
-               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;
-               }
-
-               if (ticks < difference) {
-                       ticks = BBT_Time::ticks_per_beat - ticks;
-               } else {
-                       ticks -= difference;
-               }
-
-       } 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));
-                       }
-
-               } 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));
-                       } else {
-                               ticks = lrint (ticks - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
-                       }
-               } else {
-                       /* on the subdivision, do nothing */
-               }
-       }
-
-       const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
-
-       return ret_frame;
-}
-
-framepos_t
-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_minute_locked (_metrics, minute_at_frame (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;
-
-       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 */
-
-               } 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;
-               }
-
-               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) */
@@ -3626,7 +4004,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 MusicSample (0, 0);
                                }
                                /* step back to previous beat */
                                --beats;
                                }
                                /* step back to previous beat */
                                --beats;
@@ -3641,91 +4019,118 @@ TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMo
                }
        }
 
                }
        }
 
-       const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
+       MusicSample ret (0, 0);
+       ret.sample = sample_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
-TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
+MusicSample
+TempoMap::round_to_type (samplepos_t sample, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
-       BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
+       const double minute = minute_at_sample (sample);
+       const double beat_at_samplepos = max (0.0, beat_at_minute_locked (_metrics, minute));
+       BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_samplepos));
+       MusicSample ret (0, 0);
 
        switch (type) {
        case Bar:
 
        switch (type) {
        case Bar:
+               ret.division = -1;
+
                if (dir < 0) {
                if (dir < 0) {
-                       /* find bar previous to 'frame' */
+                       /* find bar previous to 'sample' */
+                       if (bbt.bars > 0)
+                               --bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       ret.sample = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       return ret;
 
                } else if (dir > 0) {
 
                } else if (dir > 0) {
-                       /* find bar following 'frame' */
+                       /* find bar following 'sample' */
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       ret.sample = sample_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_minute (minute_at_bbt_locked (_metrics, bbt));
+                       samplepos_t raw_ft = sample_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_minute (minute_at_bbt_locked (_metrics, bbt));
+                       samplepos_t prev_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
                        ++bbt.bars;
                        ++bbt.bars;
-                       framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+                       samplepos_t next_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
+
+                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
+                               ret.sample = 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.sample = prev_ft;
+
+                               return ret;
                        }
                }
 
                break;
 
        case Beat:
                        }
                }
 
                break;
 
        case Beat:
+               ret.division = 1;
+
                if (dir < 0) {
                if (dir < 0) {
-                       return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
+                       ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos)));
+
+                       return ret;
                } else if (dir > 0) {
                } else if (dir > 0) {
-                       return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
+                       ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_samplepos)));
+
+                       return ret;
                } else {
                } else {
-                       return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
+                       ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos + 0.5)));
+
+                       return ret;
                }
                break;
        }
 
                }
                break;
        }
 
-       return 0;
+       return MusicSample (0, 0);
 }
 
 void
 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
 }
 
 void
 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
-                   framepos_t lower, framepos_t upper, uint32_t bar_mod)
+                   samplepos_t lower, samplepos_t upper, uint32_t bar_mod)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
-       framecnt_t pos = 0;
+       int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_sample (lower)));
+       samplecnt_t pos = 0;
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
 
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
 
-       if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
+       if (minute_at_beat_locked (_metrics, cnt) >= minute_at_sample (upper)) {
                return;
        }
        if (bar_mod == 0) {
                while (pos >= 0 && pos < upper) {
                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));
+                       pos = sample_at_minute (minute_at_beat_locked (_metrics, cnt));
+                       const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
                        const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
                        const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
-                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
+                       const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
+
+                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
                        ++cnt;
                }
        } else {
                        ++cnt;
                }
        } else {
-               BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
+               BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_sample (lower));
                bbt.beats = 1;
                bbt.ticks = 0;
 
                bbt.beats = 1;
                bbt.ticks = 0;
 
@@ -3735,21 +4140,30 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                }
 
                while (pos >= 0 && pos < upper) {
                }
 
                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_func()));
+                       pos = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
+                       const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
+                       const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
+
+                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
                        bbt.bars += bar_mod;
                }
        }
 }
 
 const TempoSection&
                        bbt.bars += bar_mod;
                }
        }
 }
 
 const TempoSection&
-TempoMap::tempo_section_at_frame (framepos_t frame) const
+TempoMap::tempo_section_at_sample (samplepos_t sample) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
+}
+
+TempoSection&
+TempoMap::tempo_section_at_sample (samplepos_t sample)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
+       return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
 }
 
 const TempoSection&
 }
 
 const TempoSection&
@@ -3781,7 +4195,35 @@ TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
 
        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
 {
@@ -3793,6 +4235,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;
                        }
@@ -3803,11 +4250,97 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be
        return *prev_t;
 }
 
        return *prev_t;
 }
 
-/* don't use this to calculate length (the tempo is only correct for this frame).
-   do that stuff based on the beat_at_frame and frame_at_beat api
+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 sample).
+   do that stuff based on the beat_at_sample and sample_at_beat api
 */
 double
 */
 double
-TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
+TempoMap::samples_per_quarter_note_at (const samplepos_t sample, const samplecnt_t sr) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
@@ -3823,26 +4356,27 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
-                       if (ts_at && (*i)->frame() > frame) {
+                       if (ts_at && (*i)->sample() > sample) {
                                ts_after = t;
                                break;
                        }
                        ts_at = t;
                }
        }
                                ts_after = t;
                                break;
                        }
                        ts_at = t;
                }
        }
+       assert (ts_at);
 
        if (ts_after) {
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame));
+               return  (60.0 * _sample_rate) / ts_at->tempo_at_minute (minute_at_sample (sample)).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->samples_per_quarter_note (_sample_rate);
 }
 
 const MeterSection&
 }
 
 const MeterSection&
-TempoMap::meter_section_at_frame (framepos_t frame) const
+TempoMap::meter_section_at_sample (samplepos_t sample) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
+       return meter_section_at_minute_locked (_metrics, minute_at_sample (sample));
 }
 
 const MeterSection&
 }
 
 const MeterSection&
@@ -3901,9 +4435,9 @@ TempoMap::meter_section_at_beat (double beat) const
 }
 
 const Meter&
 }
 
 const Meter&
-TempoMap::meter_at_frame (framepos_t frame) const
+TempoMap::meter_at_sample (samplepos_t sample) const
 {
 {
-       TempoMetric m (metric_at (frame));
+       TempoMetric m (metric_at (sample));
        return m.meter();
 }
 
        return m.meter();
 }
 
@@ -3912,13 +4446,14 @@ 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);
@@ -3944,16 +4479,29 @@ TempoMap::fix_legacy_session ()
                        if (!t->active()) {
                                continue;
                        }
                        if (!t->active()) {
                                continue;
                        }
+                       /* Ramp type never existed in the era of this tempo section */
+                       t->set_end_note_types_per_minute (t->note_types_per_minute());
 
 
-                       if (!t->movable()) {
+                       if (t->initial()) {
                                t->set_pulse (0.0);
                                t->set_minute (0.0);
                                t->set_position_lock_style (AudioTime);
                                prev_t = t;
                                t->set_pulse (0.0);
                                t->set_minute (0.0);
                                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);
@@ -3968,6 +4516,34 @@ 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->end_note_types_per_minute() < 0.0) {
+                                       prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
+                               }
+                       }
+
+                       prev_t = t;
+               }
+       }
+
+       if (prev_t) {
+               prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
+       }
+}
 
 XMLNode&
 TempoMap::get_state ()
 
 XMLNode&
 TempoMap::get_state ()
@@ -4004,7 +4580,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, _frame_rate);
+                                       TempoSection* ts = new TempoSection (*child, _sample_rate);
                                        _metrics.push_back (ts);
                                }
 
                                        _metrics.push_back (ts);
                                }
 
@@ -4018,7 +4594,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, _frame_rate);
+                                       MeterSection* ms = new MeterSection (*child, _sample_rate);
                                        _metrics.push_back (ms);
                                }
 
                                        _metrics.push_back (ms);
                                }
 
@@ -4031,11 +4607,6 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        }
                }
 
                        }
                }
 
-               if (niter == nlist.end()) {
-                       MetricSectionSorter cmp;
-                       _metrics.sort (cmp);
-               }
-
                /* check for legacy sessions where bbt was the base musical unit for tempo */
                for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        TempoSection* t;
                /* check for legacy sessions where bbt was the base musical unit for tempo */
                for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                        TempoSection* t;
@@ -4044,10 +4615,19 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                                        fix_legacy_session();
                                        break;
                                }
                                        fix_legacy_session();
                                        break;
                                }
-                               break;
+
+                               if (t->end_note_types_per_minute() < 0.0) {
+                                       fix_legacy_end_session();
+                                       break;
+                               }
                        }
                }
 
                        }
                }
 
+               if (niter == nlist.end()) {
+                       MetricSectionSorter cmp;
+                       _metrics.sort (cmp);
+               }
+
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
@@ -4060,9 +4640,9 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                                TempoSection* ts;
                                TempoSection* prev_t;
                                if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
                                TempoSection* ts;
                                TempoSection* prev_t;
                                if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                                       if (prev_m->pulse() == ms->pulse()) {
-                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
-                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
+                                       if (prev_m->beat() == ms->beat()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
                                                return -1;
                                        }
                                } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
                                                return -1;
                                        }
                                } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
@@ -4092,35 +4672,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()
+                       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()
                          << " type= " << enum_2_string (t->type()) << ") "  << " at pulse= " << t->pulse()
-                         << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
+                         << " minute= " << t->minute() << " sample= " << t->sample() << " (initial? " << t->initial() << ')'
                          << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
                        if (prev_t) {
                          << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
                        if (prev_t) {
-                               o << std::setprecision (17) << "  current      : " << t->beats_per_minute()
-                                 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
-                               o << "  previous     : " << prev_t->beats_per_minute()
-                                 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
+                               o <<  "  current start  : " << t->note_types_per_minute()
+                                 <<  "  current end  : " << t->end_note_types_per_minute()
+                                 << " | " << t->pulse() << " | " << t->sample() << " | " << t->minute() << std::endl;
+                               o << "  previous     : " << prev_t->note_types_per_minute()
+                                 << " | " << prev_t->pulse() << " | " << prev_t->sample() << " | " << prev_t->minute() << std::endl;
                                o << "  calculated   : " << prev_t->tempo_at_pulse (t->pulse())
                                o << "  calculated   : " << prev_t->tempo_at_pulse (t->pulse())
-                                 << " | " << prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute())
-                                 << " | " << frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))
-                                 << " | " << prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()) << std::endl;
+                                 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
+                                 << " | " << sample_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) {
                        o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
                        }
                        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;
+                         << " sample= " << m->sample() << " 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;
@@ -4157,19 +4738,19 @@ TempoMap::n_meters() const
 }
 
 void
 }
 
 void
-TempoMap::insert_time (framepos_t where, framecnt_t amount)
+TempoMap::insert_time (samplepos_t where, samplecnt_t amount)
 {
        for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
 {
        for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
-               if ((*i)->frame() >= where && (*i)->movable ()) {
+               if ((*i)->sample() >= where && !(*i)->initial ()) {
                        MeterSection* ms;
                        TempoSection* ts;
 
                        if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
                        MeterSection* ms;
                        TempoSection* ts;
 
                        if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
-                               gui_move_meter (ms, (*i)->frame() + amount);
+                               gui_set_meter_position (ms, (*i)->sample() + amount);
                        }
 
                        if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
                        }
 
                        if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
-                               gui_move_tempo (ts, (*i)->frame() + amount, 0);
+                               gui_set_tempo_position (ts, (*i)->sample() + amount, 0);
                        }
                }
        }
                        }
                }
        }
@@ -4178,7 +4759,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
 }
 
 bool
 }
 
 bool
-TempoMap::remove_time (framepos_t where, framecnt_t amount)
+TempoMap::remove_time (samplepos_t where, samplecnt_t amount)
 {
        bool moved = false;
 
 {
        bool moved = false;
 
@@ -4191,7 +4772,7 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-                       if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
+                       if ((*i)->sample() >= where && (*i)->sample() < where+amount) {
                                metric_kill_list.push_back(*i);
                                TempoSection *lt = dynamic_cast<TempoSection*> (*i);
                                if (lt)
                                metric_kill_list.push_back(*i);
                                TempoSection *lt = dynamic_cast<TempoSection*> (*i);
                                if (lt)
@@ -4200,10 +4781,10 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
                                if (lm)
                                        last_meter = lm;
                        }
                                if (lm)
                                        last_meter = lm;
                        }
-                       else if ((*i)->frame() >= where) {
+                       else if ((*i)->sample() >= where) {
                                // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
                                // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
-                               (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
-                               if ((*i)->frame() == where) {
+                               (*i)->set_minute ((*i)->minute() - minute_at_sample (amount));
+                               if ((*i)->sample() == where) {
                                        // marker was immediately after end of range
                                        tempo_after = dynamic_cast<TempoSection*> (*i);
                                        meter_after = dynamic_cast<MeterSection*> (*i);
                                        // marker was immediately after end of range
                                        tempo_after = dynamic_cast<TempoSection*> (*i);
                                        meter_after = dynamic_cast<MeterSection*> (*i);
@@ -4215,12 +4796,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_minute (minute_at_frame (where));
+                       last_tempo->set_minute (minute_at_sample (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_minute (minute_at_frame (where));
+                       last_meter->set_minute (minute_at_sample (where));
                        moved = true;
                }
 
                        moved = true;
                }
 
@@ -4238,23 +4819,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 sample position, and return the result in samples.
  *  pos can be -ve, if required.
  */
  *  pos can be -ve, if required.
  */
-framepos_t
-TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
+samplepos_t
+TempoMap::samplepos_plus_qn (samplepos_t sample, Temporal::Beats beats) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+       const double sample_qn = pulse_at_minute_locked (_metrics, minute_at_sample (sample)) * 4.0;
 
 
-       return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
+       return sample_at_minute (minute_at_pulse_locked (_metrics, (sample_qn + beats.to_double()) / 4.0));
 }
 
 }
 
-framepos_t
-TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
+samplepos_t
+TempoMap::samplepos_plus_bbt (samplepos_t pos, BBT_Time op) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
+       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_sample (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;
@@ -4270,18 +4852,18 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
        pos_bbt.bars += op.bars;
 
        }
        pos_bbt.bars += op.bars;
 
-       return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
+       return sample_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_qn (framepos_t pos, framecnt_t distance) const
+Temporal::Beats
+TempoMap::framewalk_to_qn (samplepos_t pos, samplecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return Evoral::Beats (quarter_note_at_minute_locked (_metrics, minute_at_frame (pos + distance)) - quarter_note_at_minute_locked (_metrics, minute_at_frame (pos)));
+       return Temporal::Beats (quarter_notes_between_samples_locked (_metrics, pos, pos + distance));
 }
 
 struct bbtcmp {
 }
 
 struct bbtcmp {
@@ -4297,13 +4879,13 @@ 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&
 operator<< (std::ostream& o, const MetricSection& section) {
 
 }
 
 std::ostream&
 operator<< (std::ostream& o, const MetricSection& section) {
 
-       o << "MetricSection @ " << section.frame() << ' ';
+       o << "MetricSection @ " << section.sample() << ' ';
 
        const TempoSection* ts;
        const MeterSection* ms;
 
        const TempoSection* ts;
        const MeterSection* ms;