more documentation in tempo.cc, fix constant bug in pulse_at_tempo_locked()
[ardour.git] / libs / ardour / tempo.cc
index de220b6ed729b3e79764c2bf6485cea855c0b57c..07c008ab7ce8a0d805047f170236cf55a05a20b3 100644 (file)
@@ -47,6 +47,18 @@ using Timecode::BBT_Time;
 Meter    TempoMap::_default_meter (4.0, 4.0);
 Tempo    TempoMap::_default_tempo (120.0);
 
+framepos_t
+MetricSection::frame_at_minute (const double& time) const
+{
+       return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
+}
+
+double
+MetricSection::minute_at_frame (const framepos_t& frame) const
+{
+       return (frame / (double) _sample_rate) / 60.0;
+}
+
 /***********************************************************************/
 
 double
@@ -72,8 +84,8 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
 
 const string TempoSection::xml_state_node_name = "Tempo";
 
-TempoSection::TempoSection (const XMLNode& node)
-       : MetricSection (0.0, 0, MusicTime, true)
+TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
+       : MetricSection (0.0, 0, MusicTime, true, sample_rate)
        , Tempo (TempoMap::default_tempo())
        , _c_func (0.0)
        , _active (true)
@@ -84,6 +96,7 @@ TempoSection::TempoSection (const XMLNode& node)
        BBT_Time bbt;
        double pulse;
        uint32_t frame;
+       double minute;
 
        _legacy_bbt = BBT_Time (0, 0, 0);
 
@@ -111,7 +124,15 @@ TempoSection::TempoSection (const XMLNode& node)
                if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
                        error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
                } else {
-                       set_frame (frame);
+                       set_minute (minute_at_frame (frame));
+               }
+       }
+
+       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);
                }
        }
 
@@ -132,7 +153,7 @@ TempoSection::TempoSection (const XMLNode& node)
                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 ((prop = node.property ("movable")) == 0) {
@@ -183,6 +204,8 @@ TempoSection::get_state() const
        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);
@@ -204,87 +227,83 @@ TempoSection::set_type (Type type)
        _type = type;
 }
 
-/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
+/** returns the tempo in beats per minute at the zero-based (relative to session) minute.
 */
 double
-TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::tempo_at_minute (const double& m) const
 {
 
        if (_type == Constant || _c_func == 0.0) {
-               return pulses_per_minute();
+               return beats_per_minute();
        }
 
-       return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
+       return _tempo_at_time (m - minute());
 }
 
-/** returns the zero-based frame (relative to session)
-   where the tempo in whole pulses per minute occurs in this section.
-   pulse p is only used for constant tempos.
+/** 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.
 */
-framepos_t
-TempoSection::frame_at_tempo (const double& ppm, const double& p, const framecnt_t& frame_rate) const
+double
+TempoSection::minute_at_tempo (const double& bpm, const double& p) const
 {
        if (_type == Constant || _c_func == 0.0) {
-               return ((p - pulse()) * frames_per_pulse (frame_rate))  + frame();
+               return (((p - pulse())  * note_type()) / beats_per_minute()) + minute();
        }
 
-       return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
+       return _time_at_tempo (bpm) + minute();
 }
-/** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
+/** returns the tempo in beats per minute at the supplied pulse.
 */
 double
 TempoSection::tempo_at_pulse (const double& p) const
 {
 
        if (_type == Constant || _c_func == 0.0) {
-               return pulses_per_minute();
+               return beats_per_minute();
        }
-       double const ppm = pulse_tempo_at_pulse (p - pulse());
-       return ppm;
+
+       return _tempo_at_pulse (p - pulse());
 }
 
-/** returns the zero-based beat (relative to session)
-   where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
-   note that the session tempo map may have multiple beats at a given tempo.
+/** returns the 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.
 */
 double
-TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::pulse_at_tempo (const double& bpm, const double& m) const
 {
        if (_type == Constant || _c_func == 0.0) {
-               double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
-               return  pulses;
+               const double pulses = (((m - minute()) * beats_per_minute()) / note_type()) + pulse();
+               return pulses;
        }
-       return pulse_at_pulse_tempo (ppm) + pulse();
+
+       return _pulse_at_tempo (bpm) + pulse();
 }
 
-/** returns the zero-based pulse (relative to session origin)
-   where the zero-based frame (relative to session)
-   lies.
+/** returns the pulse at the supplied session-relative minute.
 */
 double
-TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::pulse_at_minute (const double& m) const
 {
        if (_type == Constant || _c_func == 0.0) {
-               return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+               return (((m - minute()) * beats_per_minute()) / _note_type) + pulse();
        }
 
-       return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
+       return _pulse_at_time (m - minute()) + pulse();
 }
 
-/** returns the zero-based frame (relative to session start frame)
-   where the zero-based pulse (relative to session start)
-   falls.
+/** returns the minute (relative to session start) at the supplied pulse.
 */
-
-framepos_t
-TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
+double
+TempoSection::minute_at_pulse (const double& p) const
 {
        if (_type == Constant || _c_func == 0.0) {
-               return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+               return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
        }
 
-       return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
+       return _time_at_pulse (p - pulse()) + minute();
 }
 
 /*
@@ -363,97 +382,85 @@ https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Te
 */
 
 /*
-  compute this ramp's function constant using the end tempo (in whole pulses per minute)
+  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.
 */
 double
-TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
+TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse) const
 {
-       double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
-       return pulses_per_minute() *  (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
+       double const log_tempo_ratio = log (end_bpm / beats_per_minute());
+       return (beats_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
 }
 
-/* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
+/* compute the function constant from some later tempo section, given tempo (quarter notes/min.) and distance (in frames) from session origin */
 double
-TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
+TempoSection::compute_c_func_minute (const double& end_bpm, const double& end_minute) const
 {
-       return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
-}
-
-framepos_t
-TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
-{
-       return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
-}
-
-double
-TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
-{
-       return (frame / (double) frame_rate) / 60.0;
+       return c_func (end_bpm, end_minute - minute());
 }
 
 /* position function */
 double
-TempoSection::a_func (double end_ppm, double c_func) const
+TempoSection::a_func (double end_bpm, double c_func) const
 {
-       return log (end_ppm / pulses_per_minute()) /  c_func;
+       return log (end_bpm / beats_per_minute()) / c_func;
 }
 
 /*function constant*/
 double
-TempoSection::c_func (double end_ppm, double end_time) const
+TempoSection::c_func (double end_bpm, double end_time) const
 {
-       return log (end_ppm / pulses_per_minute()) /  end_time;
+       return log (end_bpm / beats_per_minute()) / end_time;
 }
 
-/* tempo in ppm at time in minutes */
+/* tempo in bpm at time in minutes */
 double
-TempoSection::pulse_tempo_at_time (const double& time) const
+TempoSection::_tempo_at_time (const double& time) const
 {
-       return exp (_c_func * time) * pulses_per_minute();
+       return exp (_c_func * time) * beats_per_minute();
 }
 
-/* time in minutes at tempo in ppm */
+/* time in minutes at tempo in bpm */
 double
-TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
+TempoSection::_time_at_tempo (const double& tempo) const
 {
-       return log (pulse_tempo / pulses_per_minute()) / _c_func;
+       return log (tempo / beats_per_minute()) / _c_func;
 }
 
-/* tick at tempo in ppm */
+/* pulse at tempo in bpm */
 double
-TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
+TempoSection::_pulse_at_tempo (const double& tempo) const
 {
-       return (pulse_tempo - pulses_per_minute()) / _c_func;
+       return ((tempo - beats_per_minute()) / _c_func) / _note_type;
 }
 
-/* tempo in ppm at tick */
+/* tempo in bpm at pulse */
 double
-TempoSection::pulse_tempo_at_pulse (const double& pulse) const
+TempoSection::_tempo_at_pulse (const double& pulse) const
 {
-       return (pulse * _c_func) + pulses_per_minute();
+       return (pulse * _note_type * _c_func) + beats_per_minute();
 }
 
 /* pulse at time in minutes */
 double
-TempoSection::pulse_at_time (const double& time) const
+TempoSection::_pulse_at_time (const double& time) const
 {
-       return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
+       return expm1 (_c_func * time) * (beats_per_minute() / (_c_func * _note_type));
 }
 
 /* time in minutes at pulse */
 double
-TempoSection::time_at_pulse (const double& pulse) const
+TempoSection::_time_at_pulse (const double& pulse) const
 {
-       return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
+       return log1p ((_c_func * pulse * _note_type) / beats_per_minute()) / _c_func;
 }
 
 /***********************************************************************/
 
 const string MeterSection::xml_state_node_name = "Meter";
 
-MeterSection::MeterSection (const XMLNode& node)
-       : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
+MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
+       : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
 {
        XMLProperty const * prop;
        LocaleGuard lg;
@@ -462,6 +469,7 @@ MeterSection::MeterSection (const XMLNode& node)
        double beat = 0.0;
        framepos_t frame = 0;
        pair<double, BBT_Time> start;
+       double minute = 0.0;
 
        if ((prop = node.property ("start")) != 0) {
                if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
@@ -508,7 +516,14 @@ MeterSection::MeterSection (const XMLNode& node)
                if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
                        error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
                } else {
-                       set_frame (frame);
+                       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);
                }
        }
 
@@ -573,6 +588,8 @@ MeterSection::get_state() const
        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);
@@ -590,32 +607,31 @@ MeterSection::get_state() const
   https://www.youtube.com/watch?v=9wQK6zMJOoQ
 
   Tempo is the rate of the musical pulse.
-  Meters divide the pulses into measures and beats.
+  Meter divides pulse into measures and beats.
 
-  TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
-  and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
-  Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
-  a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
+  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).
 
-  MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
+  MeterSecion - divides pulse into measures (via divisions_per_bar) and beats (via note_divisor).
 
-  Both tempos and meters have a pulse position and a frame position.
-  Meters also have a beat position, which is always 0.0 for the first meter.
-  TempoSections and MeterSections may be locked to either audio or music (position lock style).
+  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).
   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.
 
-  With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
+  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.
 
-  Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
+  Beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
   Beat to frame conversion of course requires the use of meter and tempo.
 
   Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
@@ -637,7 +653,7 @@ MeterSection::get_state() const
   Music and Audio
 
   Music and audio-locked objects may seem interchangeable on the surface, but when translating
-  between audio samples and beats, keep in mind that a sample is only a quantised approximation
+  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.
@@ -688,8 +704,8 @@ TempoMap::TempoMap (framecnt_t fr)
        _frame_rate = fr;
        BBT_Time start (1, 1, 0);
 
-       TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
-       MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
+       TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, 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);
@@ -711,6 +727,18 @@ TempoMap::~TempoMap ()
        _metrics.clear();
 }
 
+framepos_t
+TempoMap::frame_at_minute (const double time) const
+{
+       return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
+}
+
+double
+TempoMap::minute_at_frame (const framepos_t frame) const
+{
+       return (frame / (double) _frame_rate) / 60.0;
+}
+
 void
 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 {
@@ -931,14 +959,14 @@ TempoMap::do_insert (MetricSection* section)
                //dump (_metrics, std::cout);
        }
 }
-
+/* 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)
 {
        TempoSection* ts = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
+               ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
        }
 
 
@@ -957,11 +985,11 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
                TempoSection& first (first_tempo());
                if (ts.frame() != first.frame()) {
                        remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
+                       add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
                } else {
                        first.set_type (type);
                        first.set_pulse (0.0);
-                       first.set_frame (frame);
+                       first.set_minute (minute_at_frame (frame));
                        first.set_position_lock_style (AudioTime);
                        {
                                /* cannot move the first tempo section */
@@ -975,10 +1003,10 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
 }
 
 TempoSection*
-TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
+TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
                            , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
 {
-       TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
+       TempoSection* t = new TempoSection (pulse, minute, tempo.beats_per_minute(), tempo.note_type(), type, pls, _frame_rate);
        t->set_locked_to_meter (locked_to_meter);
        bool solved = false;
 
@@ -986,7 +1014,7 @@ TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
 
        if (recompute) {
                if (pls == AudioTime) {
-                       solved = solve_map_frame (_metrics, t, t->frame());
+                       solved = solve_map_minute (_metrics, t, t->minute());
                } else {
                        solved = solve_map_pulse (_metrics, t, t->pulse());
                }
@@ -1001,12 +1029,12 @@ TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
 }
 
 MeterSection*
-TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
+TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
 {
        MeterSection* m = 0;
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               m = add_meter_locked (meter, beat, where, frame, pls, true);
+               m = add_meter_locked (meter, beat, where, pls, true);
        }
 
 
@@ -1021,7 +1049,7 @@ TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT
 }
 
 void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
@@ -1029,7 +1057,7 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 
                if (ms.movable()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, beat, where, frame, pls, true);
+                       add_meter_locked (meter, beat, where, pls, true);
                } else {
                        MeterSection& first (first_meter());
                        TempoSection& first_t (first_tempo());
@@ -1037,10 +1065,10 @@ 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);
-                       first.set_frame (frame);
+                       //first.set_minute (minute_at_frame (frame));
                        pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
                        first.set_beat (beat);
-                       first_t.set_frame (first.frame());
+                       first_t.set_minute (first.minute());
                        first_t.set_pulse (0.0);
                        first_t.set_position_lock_style (AudioTime);
                        recompute_map (_metrics);
@@ -1051,15 +1079,16 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 }
 
 MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
 {
-       const MeterSection& prev_m = meter_section_at_frame_locked  (_metrics, frame - 1);
+       const MeterSection& prev_m = meter_section_at_minute_locked  (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
        const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
+       const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
        TempoSection* mlt = 0;
 
        if (pls == AudioTime) {
                /* add meter-locked tempo */
-               mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse,  frame, TempoSection::Ramp, AudioTime, true, true);
+               mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse,  time_minutes, TempoSection::Ramp, AudioTime, true, true);
 
                if (!mlt) {
                        return 0;
@@ -1067,7 +1096,7 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe
 
        }
 
-       MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
+       MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
        bool solved = false;
 
        do_insert (new_meter);
@@ -1075,7 +1104,7 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe
        if (recompute) {
 
                if (pls == AudioTime) {
-                       solved = solve_map_frame (_metrics, new_meter, frame);
+                       solved = solve_map_minute (_metrics, new_meter, time_minutes);
                } else {
                        solved = solve_map_bbt (_metrics, new_meter, where);
                        /* required due to resetting the pulse of meter-locked tempi above.
@@ -1270,14 +1299,14 @@ TempoMap::recompute_tempi (Metrics& metrics)
                        }
                        if (prev_t) {
                                if (t->position_lock_style() == AudioTime) {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
                                        }
 
                                } else {
-                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
-                                       t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
+                                       prev_t->set_c_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()));
 
                                }
                        }
@@ -1359,7 +1388,7 @@ TempoMap::recompute_meters (Metrics& metrics)
 
                                meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
-                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+                               meter->set_minute (minute_at_pulse_locked (metrics, pulse));
                        }
 
                        prev_m = meter;
@@ -1372,15 +1401,6 @@ TempoMap::recompute_map (Metrics& metrics, framepos_t end)
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
-       if (end < 0) {
-
-               /* we will actually stop once we hit
-                  the last metric.
-               */
-               end = max_framepos;
-
-       }
-
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
        if (end == 0) {
@@ -1453,9 +1473,10 @@ TempoMap::metric_at (BBT_Time bbt) const
        return m;
 }
 
-/** Returns the beat duration corresponding to the supplied frame, possibly returning a negative value.
+/** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
  * @param frame The session frame position.
  * @return The beat duration according to the tempo map at the supplied frame.
+ *
  * If the supplied frame lies before the first meter, the returned beat duration will be negative.
  * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
  *
@@ -1465,20 +1486,21 @@ double
 TempoMap::beat_at_frame (const framecnt_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_frame_locked (_metrics, frame);
+
+       return beat_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 /* This function uses both tempo and meter.*/
 double
-TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
-       const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
+       const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
-                       if (prev_m && (*i)->frame() > frame) {
+                       if (prev_m && (*i)->minute() > minute) {
                                next_m = static_cast<MeterSection*> (*i);
                                break;
                        }
@@ -1486,7 +1508,7 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
                }
        }
 
-       const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+       const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
 
        /* audio locked meters fake their beat */
        if (next_m && next_m->beat() < beat) {
@@ -1496,16 +1518,23 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
        return beat;
 }
 
+/** Returns the frame corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
+ *
+ * This function uses both tempo and meter.
+ */
 framepos_t
 TempoMap::frame_at_beat (const double& beat) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_beat_locked (_metrics, beat);
+
+       return frame_at_minute (minute_at_beat_locked (_metrics, beat));
 }
 
 /* meter & tempo section based */
-framepos_t
-TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
+double
+TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
        MeterSection* prev_m = 0;
        TempoSection* prev_t = 0;
@@ -1535,32 +1564,38 @@ TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) cons
 
        }
 
-       return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
+       return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
 }
 
+/** Returns a Tempo corresponding to the supplied frame position.
+ * @param frame The audio frame.
+ * @return a Tempo according to the tempo map at the supplied frame.
+ *
+ */
 Tempo
 TempoMap::tempo_at_frame (const framepos_t& frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_at_frame_locked (_metrics, frame);
+
+       return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 Tempo
-TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
        TempoSection* prev_t = 0;
 
        TempoSection* t;
 
-       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
                        if (!t->active()) {
                                continue;
                        }
-                       if ((prev_t) && t->frame() > frame) {
+                       if ((prev_t) && t->minute() > minute) {
                                /* t is the section past frame */
-                               const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
+                               const double ret_bpm = prev_t->tempo_at_minute (minute);
                                const Tempo ret_tempo (ret_bpm, prev_t->note_type());
                                return ret_tempo;
                        }
@@ -1583,19 +1618,85 @@ framepos_t
 TempoMap::frame_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_tempo_locked (_metrics, tempo);
+
+       return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
 }
 
+double
+TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
+{
+       TempoSection* prev_t = 0;
+       const double tempo_bpm = tempo.beats_per_minute();
 
-framepos_t
-TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+
+                       if (!t->active()) {
+                               continue;
+                       }
+
+                       const double t_bpm = t->beats_per_minute();
+
+                       if (t_bpm == tempo_bpm) {
+                               return t->minute();
+                       }
+
+                       if (prev_t) {
+                               const double prev_t_bpm = prev_t->beats_per_minute();
+
+                               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());
+                               }
+                       }
+                       prev_t = t;
+               }
+       }
+
+       return prev_t->minute();
+}
+
+Tempo
+TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+       TempoSection* prev_t = 0;
+
+       TempoSection* t;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((*i)->is_tempo()) {
+                       t = static_cast<TempoSection*> (*i);
+                       if (!t->active()) {
+                               continue;
+                       }
+                       if ((prev_t) && t->pulse() > pulse) {
+                               /* t is the section past frame */
+                               const double ret_bpm = prev_t->tempo_at_pulse (pulse);
+                               const Tempo ret_tempo (ret_bpm, prev_t->note_type());
+                               return ret_tempo;
+                       }
+                       prev_t = t;
+               }
+       }
+
+       const double ret = prev_t->beats_per_minute();
+       const Tempo ret_tempo (ret, prev_t->note_type ());
+
+       return ret_tempo;
+}
+
+double
+TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
 {
        TempoSection* prev_t = 0;
-       const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
+       const double tempo_bpm = tempo.beats_per_minute();
 
        Metrics::const_iterator i;
 
-       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
                if ((*i)->is_tempo()) {
                        t = static_cast<TempoSection*> (*i);
@@ -1604,28 +1705,29 @@ TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con
                                continue;
                        }
 
-                       const double t_ppm = t->beats_per_minute() / t->note_type();
+                       const double t_bpm = t->beats_per_minute();
 
-                       if (t_ppm == tempo_ppm) {
-                               return t->frame();
+                       if (t_bpm == tempo_bpm) {
+                               return t->pulse();
                        }
 
                        if (prev_t) {
-                               const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
+                               const double prev_t_bpm = prev_t->beats_per_minute();
 
-                               if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
-                                       return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
+                               if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
+                                       return prev_t->pulse_at_tempo (tempo_bpm, prev_t->minute());
                                }
                        }
                        prev_t = t;
                }
        }
 
-       return prev_t->frame();
+       return prev_t->pulse();
 }
 
-/** more precise than doing tempo_at_frame (frame_at_beat (b)),
- *  as there is no intermediate frame rounding.
+/** Returns a Tempo corresponding to the supplied position in quarter-note beats.
+ * @param qn the position in quarter note beats.
+ * @return the Tempo at the supplied quarter-note.
  */
 Tempo
 TempoMap::tempo_at_beat (const double& beat) const
@@ -1633,18 +1735,33 @@ TempoMap::tempo_at_beat (const double& beat) const
        Glib::Threads::RWLock::ReaderLock lm (lock);
        const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
        const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
-       const double note_type = prev_t->note_type();
 
-       return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
+       return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()), prev_t->note_type());
 }
 
+/** Returns the position in quarter-note beats corresponding to the supplied Tempo.
+ * @param tempo the tempo.
+ * @return the position in quarter-note beats where the map bpm
+ * is equal to that of the Tempo. currently ignores note_type.
+ */
 double
-TempoMap::pulse_at_beat (const double& beat) const
+TempoMap::beat_at_tempo (const Tempo& tempo) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_beat_locked (_metrics, beat);
+       const double pulse = pulse_at_tempo_locked (_metrics, tempo);
+
+       return beat_at_pulse_locked (_metrics, pulse);
 }
 
+/** Returns the whole-note pulse corresponding to the supplied  BBT (meter-based) beat.
+ * @param metrics the list of metric sections used to calculate the pulse.
+ * @param beat The BBT (meter-based) beat.
+ * @return the whole-note pulse at the supplied BBT (meter-based) beat.
+ *
+ * a pulse or whole note is the base musical position of a MetricSection.
+ * it is equivalent to four quarter notes.
+ *
+ */
 double
 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
@@ -1653,13 +1770,14 @@ TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) cons
        return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
 }
 
-double
-TempoMap::beat_at_pulse (const double& pulse) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return beat_at_pulse_locked (_metrics, pulse);
-}
-
+/** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
+ * @param metrics the list of metric sections used to calculate the beat.
+ * @param pulse the whole-note pulse.
+ * @return the meter-based beat at the supplied whole-note pulse.
+ *
+ * a pulse or whole note is the base musical position of a MetricSection.
+ * it is equivalent to four quarter notes.
+ */
 double
 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
@@ -1670,9 +1788,7 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (prev_m && m->pulse() > pulse) {
-                               if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
-                                       break;
-                               }
+                               break;
                        }
                        prev_m = m;
                }
@@ -1682,16 +1798,9 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con
        return ret;
 }
 
-double
-TempoMap::pulse_at_frame (const framepos_t& frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return pulse_at_frame_locked (_metrics, frame);
-}
-
 /* tempo section based */
 double
-TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
        /* HOLD (at least) THE READER LOCK */
        TempoSection* prev_t = 0;
@@ -1703,9 +1812,13 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev_t && t->frame() > frame) {
+                       if (prev_t && t->minute() > minute) {
                                /*the previous ts is the one containing the frame */
-                               const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
+                               const double ret = prev_t->pulse_at_minute (minute);
+                               /* audio locked section in new meter*/
+                               if (t->pulse() < ret) {
+                                       return t->pulse();
+                               }
                                return ret;
                        }
                        prev_t = t;
@@ -1713,21 +1826,14 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame
        }
 
        /* treated as constant for this ts */
-       const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
+       const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->beats_per_minute()) / prev_t->note_type();
 
        return pulses_in_section + prev_t->pulse();
 }
 
-framepos_t
-TempoMap::frame_at_pulse (const double& pulse) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return frame_at_pulse_locked (_metrics, pulse);
-}
-
 /* tempo section based */
-framepos_t
-TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+double
+TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
 {
        /* HOLD THE READER LOCK */
 
@@ -1742,18 +1848,23 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co
                                continue;
                        }
                        if (prev_t && t->pulse() > pulse) {
-                               return prev_t->frame_at_pulse (pulse, _frame_rate);
+                               return prev_t->minute_at_pulse (pulse);
                        }
 
                        prev_t = t;
                }
        }
        /* must be treated as constant, irrespective of _type */
-       double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
+       double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->beats_per_minute();
 
-       return (framecnt_t) floor (dtime) + prev_t->frame();
+       return dtime + prev_t->minute();
 }
 
+/** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
+ * @param bbt The BBT time (meter-based).
+ * @return bbt The BBT beat (meter-based) at the supplied BBT time.
+ *
+ */
 double
 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
 {
@@ -1794,11 +1905,16 @@ TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time&
        return ret;
 }
 
+/** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The BBT time (meter-based) at the supplied meter-based beat.
+ *
+ */
 Timecode::BBT_Time
-TempoMap::bbt_at_beat (const double& beats)
+TempoMap::bbt_at_beat (const double& beat)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_at_beat_locked (_metrics, beats);
+       return bbt_at_beat_locked (_metrics, beat);
 }
 
 Timecode::BBT_Time
@@ -1853,24 +1969,24 @@ TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
        return ret;
 }
 
+/** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
+ * @param bbt The BBT time (meter-based).
+ * @return the quarter note beat at the supplied BBT time
+ *
+ * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
+ *
+ * while the input uses meter, the output does not.
+ */
 double
-TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return pulse_at_bbt_locked (_metrics, bbt);
-}
-
-double
-TempoMap::pulse_at_bbt_rt (const Timecode::BBT_Time& bbt)
+TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        if (!lm.locked()) {
-               throw std::logic_error ("TempoMap::pulse_at_bbt_rt() could not lock tempo map");
+               throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
        }
 
-       return pulse_at_bbt_locked (_metrics, bbt);
+       return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
 }
 
 double
@@ -1904,14 +2020,15 @@ TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time&
        return ret;
 }
 
-Timecode::BBT_Time
-TempoMap::bbt_at_pulse (const double& pulse)
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return bbt_at_pulse_locked (_metrics, pulse);
-}
-
+/** 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
 {
@@ -1965,6 +2082,11 @@ TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) cons
        return ret;
 }
 
+/** Returns the BBT time corresponding to the supplied frame position.
+ * @param frame the position in audio samples.
+ * @return the BBT time at the frame position .
+ *
+ */
 BBT_Time
 TempoMap::bbt_at_frame (framepos_t frame)
 {
@@ -1978,7 +2100,7 @@ TempoMap::bbt_at_frame (framepos_t frame)
        }
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return bbt_at_frame_locked (_metrics, frame);
+       return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 BBT_Time
@@ -1990,13 +2112,13 @@ TempoMap::bbt_at_frame_rt (framepos_t frame)
                throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
        }
 
-       return bbt_at_frame_locked (_metrics, frame);
+       return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 Timecode::BBT_Time
-TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
+TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
 {
-       if (frame < 0) {
+       if (minute < 0) {
                BBT_Time bbt;
                bbt.bars = 1;
                bbt.beats = 1;
@@ -2004,7 +2126,7 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
                return bbt;
        }
 
-       const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
+       const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
        MeterSection* prev_m = 0;
        MeterSection* next_m = 0;
 
@@ -2013,7 +2135,7 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
-                       if (prev_m && m->frame() > frame) {
+                       if (prev_m && m->minute() > minute) {
                                next_m = m;
                                break;
                        }
@@ -2021,10 +2143,10 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
                }
        }
 
-       double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+       double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
 
        /* handle frame before first meter */
-       if (frame < prev_m->frame()) {
+       if (minute < prev_m->minute()) {
                beat = 0.0;
        }
        /* audio locked meters fake their beat */
@@ -2063,6 +2185,11 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame)
        return ret;
 }
 
+/** Returns the frame position corresponding to the supplied BBT time.
+ * @param bbt the position in BBT time.
+ * @return the frame position at bbt.
+ *
+ */
 framepos_t
 TempoMap::frame_at_bbt (const BBT_Time& bbt)
 {
@@ -2076,45 +2203,40 @@ TempoMap::frame_at_bbt (const BBT_Time& bbt)
        }
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return frame_at_bbt_locked (_metrics, bbt);
+       return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
 }
 
 /* meter & tempo section based */
-framepos_t
-TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
+double
+TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
 {
        /* HOLD THE READER LOCK */
 
-       const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
+       const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
        return ret;
 }
 
 /**
- * Returns the distance from 0 in quarter pulses at the supplied frame.
+ * Returns the quarter-note beat position corresponding to the supplied frame.
  *
- * Plugin APIs don't count ticks in the same way PROGRAM_NAME does.
- * We use ticks per beat whereas the rest of the world uses ticks per quarter note.
- * This is more or less the VST's ppqPos (a scalar you use to obtain tick position
- * in whatever ppqn you're using).
+ * @param frame the position in frames.
+ * @return The quarter-note position of the supplied frame. Ignores meter.
  *
- * @param frame The distance in frames relative to session 0 whose quarter note distance you would like.
- * @return The quarter note (quarter pulse) distance from session 0 to the supplied frame. Ignores meter.
 */
-
 double
 TempoMap::quarter_note_at_frame (const framepos_t frame)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double ret = quarter_note_at_frame_locked (_metrics, frame);
+       const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
 
        return ret;
 }
 
 double
-TempoMap::quarter_note_at_frame_locked (const Metrics& metrics, const framepos_t frame)
+TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
 {
-       const double ret = pulse_at_frame_locked (metrics, frame) * 4.0;
+       const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
 
        return ret;
 }
@@ -2128,33 +2250,48 @@ TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
                throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
        }
 
-       const double ret = pulse_at_frame_locked (_metrics, frame) * 4.0;
+       const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
 
        return ret;
 }
 
+/**
+ * Returns the frame position corresponding to the supplied quarter-note beat.
+ *
+ * @param quarter_note the quarter-note position.
+ * @return the frame position of the supplied quarter-note. Ignores meter.
+ *
+ *
+*/
 framepos_t
 TempoMap::frame_at_quarter_note (const double quarter_note)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const framepos_t ret = frame_at_quarter_note_locked (_metrics, quarter_note);
+       const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
 
        return ret;
 }
 
-framepos_t
-TempoMap::frame_at_quarter_note_locked (const Metrics& metrics, const double quarter_note)
+double
+TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
 {
-       const framepos_t ret = frame_at_pulse_locked (metrics, quarter_note / 4.0);
+       const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
 
        return ret;
 }
 
+/** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
+ * @param beat The BBT (meter-based) beat.
+ * @return The quarter-note position of the supplied BBT (meter-based) beat.
+ *
+ * a quarter-note may be compared with and assigned to Evoral::Beats.
+ *
+ */
 double
 TempoMap::quarter_note_at_beat (const double beat)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
        const double ret = quarter_note_at_beat_locked (_metrics, beat);
 
@@ -2162,23 +2299,62 @@ TempoMap::quarter_note_at_beat (const double beat)
 }
 
 double
-TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat)
+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;
 }
 
+/** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
+ * @param quarter_note The position in quarter-note beats.
+ * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
+ *
+ * a quarter-note is the musical unit of Evoral::Beats.
+ *
+ */
 double
 TempoMap::beat_at_quarter_note (const double quarter_note)
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double ret = beat_at_pulse_locked (_metrics, quarter_note / 4.0);
+       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);
+}
+
+/** Returns the duration in frames between two supplied quarter-note beat positions.
+ * @param start the first position in quarter-note beats.
+ * @param end the end position in quarter-note beats.
+ * @return the frame distance ober the quarter-note beats duration.
+ *
+ * use this rather than e.g.
+ * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
+ * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
+ *
+ */
+framecnt_t
+TempoMap::frames_between_quarter_notes (const double start, const double end)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
+}
+
+double
+TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end)
+{
+
+       return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
+}
+
 bool
 TempoMap::check_solved (const Metrics& metrics) const
 {
@@ -2195,12 +2371,12 @@ TempoMap::check_solved (const Metrics& metrics) const
                        }
                        if (prev_t) {
                                /* check ordering */
-                               if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
+                               if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
                                        return false;
                                }
 
                                /* precision check ensures tempo and frames align.*/
-                               if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
+                               if (t->frame() != frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))) {
                                        if (!t->locked_to_meter()) {
                                                return false;
                                        }
@@ -2220,18 +2396,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) {
-                               const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
-                               const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
+                               const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
+                               const double nascent_m_minute = t->minute_at_pulse (m->pulse());
                                /* Here we check that a preceding section of music doesn't overlap a subsequent one.
-                                  It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
-                                  (they place an exact pulse at a particular time expressed only in frames).
-                                  This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
-                                  away from its actual frame (which is now the frame location of the exact pulse).
-                                  This can result in the calculated frame (from the previous musical section)
-                                  differing from the exact frame by one sample.
-                                  Allow for that.
                                */
-                               if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
+                               if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
                                        return false;
                                }
                        }
@@ -2269,11 +2438,11 @@ TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
 }
 
 bool
-TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
+TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
 {
        TempoSection* prev_t = 0;
        TempoSection* section_prev = 0;
-       framepos_t first_m_frame = 0;
+       double first_m_minute = 0.0;
 
        /* can't move a tempo before the first meter */
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
@@ -2281,17 +2450,17 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
                        if (!m->movable()) {
-                               first_m_frame = m->frame();
+                               first_m_minute = m->minute();
                                break;
                        }
                }
        }
-       if (section->movable() && frame <= first_m_frame) {
+       if (section->movable() && minute <= first_m_minute) {
                return false;
        }
 
        section->set_active (true);
-       section->set_frame (frame);
+       section->set_minute (minute);
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                TempoSection* t;
@@ -2310,12 +2479,12 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                                        continue;
                                }
                                if (t->position_lock_style() == MusicTime) {
-                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
-                                       t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
+                                       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()));
                                } else {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
                                        }
                                }
                        }
@@ -2324,9 +2493,9 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
        }
 
        if (section_prev) {
-               section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
+               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_frame (frame, _frame_rate));
+                       section->set_pulse (section_prev->pulse_at_tempo (section->beats_per_minute(), minute));
                }
        }
 
@@ -2378,12 +2547,12 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
                                        continue;
                                }
                                if (t->position_lock_style() == MusicTime) {
-                                       prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
-                                       t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
+                                       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()));
                                } else {
-                                       prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
+                                       prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
                                        if (!t->locked_to_meter()) {
-                                               t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
+                                               t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
                                        }
                                }
                        }
@@ -2392,8 +2561,8 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
        }
 
        if (section_prev) {
-               section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
-               section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
+               section_prev->set_c_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)
@@ -2428,17 +2597,17 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
 }
 
 bool
-TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
+TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
 {
        /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
-       const MeterSection* other =  &meter_section_at_frame_locked (imaginary, frame);
-       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
+       const MeterSection* other =  &meter_section_at_minute_locked (imaginary, minute);
+       if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
                return false;
        }
 
        if (!section->movable()) {
                /* lock the first tempo to our first meter */
-               if (!set_active_tempos (imaginary, frame)) {
+               if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
                        return false;
                }
        }
@@ -2449,7 +2618,7 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                TempoSection* t;
                if ((*ii)->is_tempo()) {
                        t = static_cast<TempoSection*> (*ii);
-                       if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
+                       if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
                                meter_locked_tempo = t;
                                break;
                        }
@@ -2471,21 +2640,24 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                        m = static_cast<MeterSection*> (*i);
                        if (m == section){
                                if (prev_m && section->movable()) {
-                                       const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
+                                       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 frame/pulse corresponding to its musical position,
+                                               /* set the section pulse according to its musical position,
                                                 * as an earlier time than this has been requested.
                                                */
                                                const double new_pulse = ((section->beat() - prev_m->beat())
                                                                          / prev_m->note_divisor()) + prev_m->pulse();
 
-                                               const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
-
-                                               if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
-                                                       meter_locked_tempo->set_pulse (new_pulse);
-                                                       solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
-                                                       section->set_frame (smallest_frame);
+                                               tempo_copy->set_position_lock_style (MusicTime);
+                                               if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
+                                                       meter_locked_tempo->set_position_lock_style (MusicTime);
+                                                       section->set_position_lock_style (MusicTime);
                                                        section->set_pulse (new_pulse);
+                                                       solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
+                                                       meter_locked_tempo->set_position_lock_style (AudioTime);
+                                                       section->set_position_lock_style (AudioTime);
+                                                       section->set_minute (meter_locked_tempo->minute());
+
                                                } else {
                                                        solved = false;
                                                }
@@ -2504,14 +2676,16 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                                                   possibly disallowed 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*> (&meter_section_at_frame_locked (future_map, section->frame()));
-                                               meter_copy->set_frame (frame);
+                                               MeterSection* meter_copy = const_cast<MeterSection*>
+                                                       (&meter_section_at_minute_locked (future_map, section->minute()));
+
+                                               meter_copy->set_minute (minute);
 
-                                               if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
-                                                       section->set_frame (frame);
+                                               if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
+                                                       section->set_minute (minute);
                                                        meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
                                                                                                / prev_m->note_divisor()) + prev_m->pulse());
-                                                       solve_map_frame (imaginary, meter_locked_tempo, frame);
+                                                       solve_map_minute (imaginary, meter_locked_tempo, minute);
                                                } else {
                                                        solved = false;
                                                }
@@ -2529,14 +2703,14 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                                } else {
                                        /* not movable (first meter atm) */
 
-                                       tempo_copy->set_frame (frame);
+                                       tempo_copy->set_minute (minute);
                                        tempo_copy->set_pulse (0.0);
 
-                                       if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
-                                               section->set_frame (frame);
-                                               meter_locked_tempo->set_frame (frame);
+                                       if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
+                                               section->set_minute (minute);
+                                               meter_locked_tempo->set_minute (minute);
                                                meter_locked_tempo->set_pulse (0.0);
-                                               solve_map_frame (imaginary, meter_locked_tempo, frame);
+                                               solve_map_minute (imaginary, meter_locked_tempo, minute);
                                        } else {
                                                solved = false;
                                        }
@@ -2603,7 +2777,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
 
                                section->set_beat (b_bbt);
                                section->set_pulse (pulse);
-                               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+                               section->set_minute (minute_at_pulse_locked (imaginary, pulse));
                                prev_m = section;
                                continue;
                        }
@@ -2660,7 +2834,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
                                new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                m->set_beat (b_bbt);
                                m->set_pulse (new_pulse);
-                               m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
+                               m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
                        }
 
                        prev_m = m;
@@ -2675,7 +2849,7 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti
 
                section->set_beat (b_bbt);
                section->set_pulse (pulse);
-               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+               section->set_minute (minute_at_pulse_locked (imaginary, pulse));
        }
 
        MetricSectionSorter cmp;
@@ -2750,7 +2924,7 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSe
 /** answers the question "is this a valid beat position for this tempo section?".
  *  it returns true if the tempo section can be moved to the requested bbt position,
  *  leaving the tempo map in a solved state.
- * @param section the tempo section to be moved
+ * @param ts the tempo section to be moved
  * @param bbt the requested new position for the tempo section
  * @return true if the tempo section can be moved to the position, otherwise false.
  */
@@ -2783,7 +2957,7 @@ TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
 * taking any possible reordering as a consequence of this into account.
 * @param section - the section to be altered
-* @param bbt - the bbt where the altered tempo will fall
+* @param bbt - the BBT time  where the altered tempo will fall
 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
 */
 pair<double, framepos_t>
@@ -2814,6 +2988,22 @@ TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
        return ret;
 }
 
+/** moves a TempoSection to a specified position.
+ * @param ts - the section to be moved
+ * @param frame - the new position in frames for the tempo
+ * @param sub_num - the snap division to use if using musical time.
+ *
+ * if sub_num is non-zero, the frame position is used to calculate an exact
+ * musical position.
+ * sub_num   | effect
+ * -1        | snap to bars (meter-based)
+ *  0        | no snap - use audio frame for musical position
+ *  1        | snap to meter-based (BBT) beat
+ * >1        | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
+ *
+ * this follows the snap convention in the gui.
+ * if sub_num is zero, the musical position will be taken from the supplied frame.
+ */
 void
 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
 {
@@ -2827,7 +3017,7 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
 
                        tempo_copy->set_position_lock_style (AudioTime);
 
-                       if (solve_map_frame (future_map, tempo_copy, frame)) {
+                       if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
                                const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
                                const double pulse = pulse_at_beat_locked (future_map, beat);
 
@@ -2844,7 +3034,7 @@ 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);
 
-                       if (solve_map_frame (future_map, tempo_copy, frame)) {
+                       if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
                                if (sub_num != 0) {
                                        /* We're moving the object that defines the grid while snapping to it...
                                         * Placing the ts at the beat corresponding to the requested frame may shift the
@@ -2867,7 +3057,7 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
                                                recompute_meters (_metrics);
                                        }
                                } else {
-                                       solve_map_frame (_metrics, ts, frame);
+                                       solve_map_minute (_metrics, ts, minute_at_frame (frame));
                                        recompute_meters (_metrics);
                                }
                        }
@@ -2883,6 +3073,14 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int&
        MetricPositionChanged (); // Emit Signal
 }
 
+/** moves a MeterSection to a specified position.
+ * @param ms - the section to be moved
+ * @param frame - the new position in frames for the meter
+ *
+ * as a meter cannot snap to anything but bars,
+ * the supplied frame is rounded to the nearest bar, possibly
+ * leaving the meter position unchanged.
+ */
 void
 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
 {
@@ -2894,8 +3092,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);
 
-                       if (solve_map_frame (future_map, copy, frame)) {
-                               solve_map_frame (_metrics, ms, frame);
+                       if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
+                               solve_map_minute (_metrics, ms, minute_at_frame (frame));
                                recompute_tempi (_metrics);
                        }
                }
@@ -2904,7 +3102,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);
 
-                       const double beat = beat_at_frame_locked (_metrics, frame);
+                       const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
                        const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
 
                        if (solve_map_bbt (future_map, copy, bbt)) {
@@ -2953,14 +3151,14 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
 }
 
 void
-TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
+TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
 {
        /*
          Ts (future prev_t)   Tnext
          |                    |
          |     [drag^]        |
          |----------|----------
-               e_f  pulse(frame)
+               e_f  qn_beats(frame)
        */
 
        Metrics future_map;
@@ -2977,7 +3175,7 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                const frameoffset_t fr_off = end_frame - frame;
 
                if (prev_t && prev_t->pulse() > 0.0) {
-                       prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
+                       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;
@@ -3005,8 +3203,8 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
 
                const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
 
-               const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
-               const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+               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));
 
                double new_bpm;
 
@@ -3054,7 +3252,7 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
 
                        double frame_ratio = 1.0;
                        double pulse_ratio = 1.0;
-                       const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
+                       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) {
@@ -3100,12 +3298,18 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
        MetricPositionChanged (); // Emit Signal
 }
 
-/** Returns the exact beat subdivision closest to the supplied frame, possibly returning a negative value.
+/** 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 requested beat subdivision to use when rounding the 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.
- * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
+ *
+ * If the supplied frame lies before the first meter, the return will be negative,
+ * in which case the returned beat uses the first meter (for BBT subdivisions) and
+ * the continuation of the tempo curve (backwards).
  *
  * This function uses both tempo and meter.
  */
@@ -3118,34 +3322,29 @@ TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
 }
 
 double
-TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
+TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
 {
-       double beat = beat_at_frame_locked (metrics, frame);
-
-       if (sub_num > 1) {
-               beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
-       } else if (sub_num == 1) {
-               /* snap to beat */
-               beat = floor (beat + 0.5);
-       } else if (sub_num == -1) {
-               /* snap to  bar */
-               Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
-               bbt.beats = 1;
-               bbt.ticks = 0;
-
-               const double prev_b = beat_at_bbt_locked (_metrics, bbt);
-               ++bbt.bars;
-               const double next_b = beat_at_bbt_locked (_metrics, bbt);
-
-               if ((beat - prev_b) > (next_b - prev_b) / 2.0) {
-                       beat = next_b;
-               } else {
-                       beat = prev_b;
-               }
-       }
-
-       return beat;
+       return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
 }
+
+/** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
+ * the supplied frame, possibly returning a negative value.
+ * Supplying a frame position with a non-zero sub_num is equivalent to supplying
+ * a quarter-note musical position without frame rounding (see below)
+ *
+ * @param frame  The session frame position.
+ * @param sub_num The subdivision to use when rounding the quarter note.
+ *                A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
+ *                Positive integers indicate quarter note (non BBT) divisions.
+ *                0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
+ * @return The quarter note position of the supplied frame.
+ *
+ * If the supplied frame lies before the first meter, the return will be negative,
+ * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
+ * the continuation of the tempo curve (backwards).
+ *
+ * This function uses both tempo and meter.
+ */
 double
 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
 {
@@ -3157,22 +3356,22 @@ TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
 double
 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
 {
-       double qn = quarter_note_at_frame_locked (metrics, frame);
+       double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
 
        if (sub_num > 1) {
                qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
        } else if (sub_num == 1) {
-               /* snap to quarter note */
-               qn = floor (qn + 0.5);
+               /* 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));
        } else if (sub_num == -1) {
                /* snap to  bar */
                Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
                bbt.beats = 1;
                bbt.ticks = 0;
 
-               const double prev_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
+               const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
                ++bbt.bars;
-               const double next_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
+               const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
 
                if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
                        qn = next_b;
@@ -3184,16 +3383,63 @@ TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& fr
        return qn;
 }
 
+/** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
+ * @param pos the frame position in the tempo map.
+ * @param bbt the distance in BBT time from pos to calculate.
+ * @param dir the rounding direction..
+ * @return the duration in frames between pos and bbt
+*/
 framecnt_t
 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double tick_at_time = max (0.0, beat_at_frame_locked (_metrics, pos)) * BBT_Time::ticks_per_beat;
-       const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
-       const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
+       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();
+
+       if (dir > 0) {
+               pos_bbt.bars += bbt.bars;
+
+               pos_bbt.ticks += bbt.ticks;
+               if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
+                       pos_bbt.beats += 1;
+                       pos_bbt.ticks -= BBT_Time::ticks_per_beat;
+               }
+
+               pos_bbt.beats += bbt.beats;
+               if ((double) pos_bbt.beats > divisions) {
+                       pos_bbt.bars += 1;
+                       pos_bbt.beats -= divisions;
+               }
+
+               return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
+       } else {
+               pos_bbt.bars -= bbt.bars;
+
+               if (pos_bbt.ticks < bbt.ticks) {
+                       if (pos_bbt.beats == 1) {
+                               pos_bbt.bars--;
+                               pos_bbt.beats = divisions;
+                       } else {
+                               pos_bbt.beats--;
+                       }
+                       pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
+               } else {
+                       pos_bbt.ticks -= bbt.ticks;
+               }
+
+               if (pos_bbt.beats <= bbt.beats) {
+                       pos_bbt.bars--;
+                       pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
+               } else {
+                       pos_bbt.beats -= bbt.beats;
+               }
 
-       return frame_at_beat_locked (_metrics, total_beats);
+               return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
+       }
+
+       return 0;
 }
 
 framepos_t
@@ -3212,7 +3458,7 @@ framepos_t
 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
+       uint32_t ticks = (uint32_t) floor (max (0.0, beat_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;
 
@@ -3299,7 +3545,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                }
        }
 
-       const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+       const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
 
        return ret_frame;
 }
@@ -3308,7 +3554,7 @@ 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_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
+       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;
 
@@ -3395,7 +3641,7 @@ TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMo
                }
        }
 
-       const framepos_t ret_frame = frame_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
+       const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
 
        return ret_frame;
 }
@@ -3405,7 +3651,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       const double beat_at_framepos = max (0.0, beat_at_frame_locked (_metrics, frame));
+       const double 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));
 
        switch (type) {
@@ -3414,22 +3660,22 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                        /* find bar previous to 'frame' */
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_bbt_locked (_metrics, bbt);
+                       return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
 
                } else if (dir > 0) {
                        /* find bar following 'frame' */
                        ++bbt.bars;
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       return frame_at_bbt_locked (_metrics, bbt);
+                       return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
                } else {
                        /* true rounding: find nearest bar */
-                       framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
                        bbt.beats = 1;
                        bbt.ticks = 0;
-                       framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
                        ++bbt.bars;
-                       framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
+                       framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
 
                        if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
                                return next_ft;
@@ -3442,11 +3688,11 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 
        case Beat:
                if (dir < 0) {
-                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
+                       return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
                } else if (dir > 0) {
-                       return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
+                       return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
                } else {
-                       return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
+                       return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
                }
                break;
        }
@@ -3456,27 +3702,45 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 
 void
 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
-                   framepos_t lower, framepos_t upper)
+                   framepos_t lower, framepos_t upper, uint32_t bar_mod)
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
+       int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
        framecnt_t pos = 0;
        /* although the map handles negative beats, bbt doesn't. */
        if (cnt < 0.0) {
                cnt = 0.0;
        }
 
-       if (frame_at_beat_locked (_metrics, cnt) >= upper) {
+       if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (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));
+                       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()));
+                       ++cnt;
+               }
+       } else {
+               BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
+               bbt.beats = 1;
+               bbt.ticks = 0;
 
-       while (pos < upper) {
-               pos = frame_at_beat_locked (_metrics, cnt);
-               const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
-               const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
-               const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
-               points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
-               ++cnt;
+               if (bar_mod != 1) {
+                       bbt.bars -= bbt.bars % bar_mod;
+                       ++bbt.bars;
+               }
+
+               while (pos >= 0 && pos < upper) {
+                       pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
+                       const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
+                       points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
+                       bbt.bars += bar_mod;
+               }
        }
 }
 
@@ -3484,11 +3748,12 @@ const TempoSection&
 TempoMap::tempo_section_at_frame (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       return tempo_section_at_frame_locked (_metrics, frame);
+
+       return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
 }
 
 const TempoSection&
-TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
+TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
 {
        TempoSection* prev = 0;
 
@@ -3501,7 +3766,7 @@ TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t fram
                        if (!t->active()) {
                                continue;
                        }
-                       if (prev && t->frame() > frame) {
+                       if (prev && t->minute() > minute) {
                                break;
                        }
 
@@ -3567,14 +3832,21 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
        }
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
+               return  (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame));
        }
        /* must be treated as constant tempo */
        return ts_at->frames_per_beat (_frame_rate);
 }
 
 const MeterSection&
-TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
+TempoMap::meter_section_at_frame (framepos_t frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
+}
+
+const MeterSection&
+TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
 {
        Metrics::const_iterator i;
        MeterSection* prev = 0;
@@ -3586,7 +3858,7 @@ TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t fram
                if (!(*i)->is_tempo()) {
                        m = static_cast<MeterSection*> (*i);
 
-                       if (prev && (*i)->frame() > frame) {
+                       if (prev && (*i)->minute() > minute) {
                                break;
                        }
 
@@ -3602,14 +3874,6 @@ TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t fram
        return *prev;
 }
 
-
-const MeterSection&
-TempoMap::meter_section_at_frame (framepos_t frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return meter_section_at_frame_locked (_metrics, frame);
-}
-
 const MeterSection&
 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
@@ -3658,7 +3922,7 @@ TempoMap::fix_legacy_session ()
                                pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                m->set_beat (bbt);
                                m->set_pulse (0.0);
-                               m->set_frame (0);
+                               m->set_minute (0.0);
                                m->set_position_lock_style (AudioTime);
                                prev_m = m;
                                continue;
@@ -3683,7 +3947,7 @@ TempoMap::fix_legacy_session ()
 
                        if (!t->movable()) {
                                t->set_pulse (0.0);
-                               t->set_frame (0);
+                               t->set_minute (0.0);
                                t->set_position_lock_style (AudioTime);
                                prev_t = t;
                                continue;
@@ -3740,7 +4004,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        if (child->name() == TempoSection::xml_state_node_name) {
 
                                try {
-                                       TempoSection* ts = new TempoSection (*child);
+                                       TempoSection* ts = new TempoSection (*child, _frame_rate);
                                        _metrics.push_back (ts);
                                }
 
@@ -3754,7 +4018,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        } else if (child->name() == MeterSection::xml_state_node_name) {
 
                                try {
-                                       MeterSection* ms = new MeterSection (*child);
+                                       MeterSection* ms = new MeterSection (*child, _frame_rate);
                                        _metrics.push_back (ms);
                                }
 
@@ -3838,17 +4102,25 @@ TempoMap::dump (const Metrics& metrics, std::ostream& o) const
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
                if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
-                         << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
-                       o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
+                       o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type()
+                         << " type= " << enum_2_string (t->type()) << ") "  << " at pulse= " << t->pulse()
+                         << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
+                         << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
                        if (prev_t) {
-                               o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
-                               o << "calculated   : " << prev_t->tempo_at_pulse (t->pulse()) *  prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
+                               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 << "  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 = t;
                } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
-                         << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
+                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
+                         << " frame= " << m->frame() << " pulse: " << m->pulse() <<  " beat : " << m->beat()
+                         << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
                }
        }
        o << "------" << std::endl;
@@ -3887,129 +4159,24 @@ TempoMap::n_meters() const
 void
 TempoMap::insert_time (framepos_t where, framecnt_t amount)
 {
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
-               for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
-                       if ((*i)->frame() >= where && (*i)->movable ()) {
-                               (*i)->set_frame ((*i)->frame() + amount);
-                       }
-               }
-
-               /* now reset the BBT time of all metrics, based on their new
-                * audio time. This is the only place where we do this reverse
-                * timestamp.
-                */
-
-               Metrics::iterator i;
-               const MeterSection* meter;
-               const TempoSection* tempo;
-               MeterSection *m;
-               TempoSection *t;
-
-               meter = &first_meter ();
-               tempo = &first_tempo ();
-
-               BBT_Time start;
-               BBT_Time end;
-
-               // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
-
-               bool first = true;
-               MetricSection* prev = 0;
-
-               for (i = _metrics.begin(); i != _metrics.end(); ++i) {
-
-                       BBT_Time bbt;
-                       //TempoMetric metric (*meter, *tempo);
-                       MeterSection* ms = const_cast<MeterSection*>(meter);
-                       TempoSection* ts = const_cast<TempoSection*>(tempo);
-                       if (prev) {
-                               if (ts){
-                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               if (!t->active()) {
-                                                       continue;
-                                               }
-                                               ts->set_pulse (t->pulse());
-                                       }
-                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               ts->set_pulse (m->pulse());
-                                       }
-                                       ts->set_frame (prev->frame());
+       for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
+               if ((*i)->frame() >= where && (*i)->movable ()) {
+                       MeterSection* ms;
+                       TempoSection* ts;
 
-                               }
-                               if (ms) {
-                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
-                                               ms->set_beat (start);
-                                               ms->set_pulse (m->pulse());
-                                       }
-                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               if (!t->active()) {
-                                                       continue;
-                                               }
-                                               const double beat = beat_at_pulse_locked (_metrics, t->pulse());
-                                               pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
-                                               ms->set_beat (start);
-                                               ms->set_pulse (t->pulse());
-                                       }
-                                       ms->set_frame (prev->frame());
-                               }
-
-                       } else {
-                               // metric will be at frames=0 bbt=1|1|0 by default
-                               // which is correct for our purpose
+                       if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
+                               gui_move_meter (ms, (*i)->frame() + amount);
                        }
 
-                       // cerr << bbt << endl;
-
-                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               if (!t->active()) {
-                                       continue;
-                               }
-                               t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
-                               tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
-                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               bbt = bbt_at_frame_locked (_metrics, m->frame());
-
-                               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
-
-                               if (first) {
-                                       first = false;
-                               } else {
-
-                                       if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
-                                               /* round up to next beat */
-                                               bbt.beats += 1;
-                                       }
-
-                                       bbt.ticks = 0;
-
-                                       if (bbt.beats != 1) {
-                                               /* round up to next bar */
-                                               bbt.bars += 1;
-                                               bbt.beats = 1;
-                                       }
-                               }
-                               pair<double, BBT_Time> start = make_pair (max (0.0, beat_at_frame_locked (_metrics, m->frame())), bbt);
-                               m->set_beat (start);
-                               m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
-                               meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
+                       if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
+                               gui_move_tempo (ts, (*i)->frame() + amount, 0);
                        }
-
-                       prev = (*i);
                }
-
-               recompute_map (_metrics);
        }
 
-
        PropertyChanged (PropertyChange ());
 }
+
 bool
 TempoMap::remove_time (framepos_t where, framecnt_t amount)
 {
@@ -4035,7 +4202,7 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
                        }
                        else if ((*i)->frame() >= where) {
                                // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
-                               (*i)->set_frame ((*i)->frame() - amount);
+                               (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
                                if ((*i)->frame() == where) {
                                        // marker was immediately after end of range
                                        tempo_after = dynamic_cast<TempoSection*> (*i);
@@ -4048,12 +4215,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);
-                       last_tempo->set_frame(where);
+                       last_tempo->set_minute (minute_at_frame (where));
                        moved = true;
                }
                if (last_meter && !meter_after) {
                        metric_kill_list.remove(last_meter);
-                       last_meter->set_frame(where);
+                       last_meter->set_minute (minute_at_frame (where));
                        moved = true;
                }
 
@@ -4071,40 +4238,23 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
        return moved;
 }
 
-/** Add some (fractional) beats to a session frame position, and return the result in frames.
+/** Add some (fractional) Beats to a session frame position, and return the result in frames.
  *  pos can be -ve, if required.
  */
 framepos_t
-TempoMap::framepos_plus_beats (framepos_t frame, Evoral::Beats beats) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double());
-}
-framepos_t
-TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double());
-}
-
-/** Subtract some (fractional) beats from a frame position, and return the result in frames */
-framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
+TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
+       return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
 }
 
-/** Add the BBT interval op to pos and return the result */
 framepos_t
 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
+       BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
        pos_bbt.ticks += op.ticks;
        if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
                ++pos_bbt.beats;
@@ -4120,27 +4270,20 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
        pos_bbt.bars += op.bars;
 
-       return frame_at_bbt_locked (_metrics, pos_bbt);
+       return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
     starting at pos.
 */
-Evoral::Beats
-TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
-}
-
 Evoral::Beats
 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
+       return Evoral::Beats (quarter_note_at_minute_locked (_metrics, minute_at_frame (pos + distance)) - quarter_note_at_minute_locked (_metrics, minute_at_frame (pos)));
 }
+
 struct bbtcmp {
     bool operator() (const BBT_Time& a, const BBT_Time& b) {
            return a < b;