Tempo ramps - use consistent naming for _locked methods, use enum_2_string for TempoS...
[ardour.git] / libs / ardour / tempo.cc
index 5dcbf3761cb5e40e877bded222d92c4cf5e03ab0..08a70c4574f9a3f699f63d742047c07aba15fe55 100644 (file)
 #include <unistd.h>
 
 #include <glibmm/threads.h>
+
+#include "pbd/enumwriter.h"
 #include "pbd/xml++.h"
 #include "evoral/Beats.hpp"
+
 #include "ardour/debug.h"
 #include "ardour/lmath.h"
 #include "ardour/tempo.h"
@@ -74,26 +77,35 @@ TempoSection::TempoSection (const XMLNode& node)
        : MetricSection (0.0), Tempo (TempoMap::default_tempo())
 {
        const XMLProperty *prop;
-       BBT_Time start;
        LocaleGuard lg;
-
-       if ((prop = node.property ("start")) == 0) {
-               error << _("MeterSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
+       BBT_Time bbt;
+       double beat;
+
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                           &bbt.bars,
+                           &bbt.beats,
+                           &bbt.ticks) == 3) {
+                       /* legacy session - start used to be in bbt*/
+                       _legacy_bbt = bbt;
+                       beat = -1.0;
+                       set_beat (beat);
+               }
+       } else {
+               warning << _("TempoSection XML node has no \"start\" property") << endmsg;
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &bbt.bars,
-                   &bbt.beats,
-                   &bbt.ticks) == 3) {
-               /* legacy session - start used to be in bbt*/
-               _legacy_bbt = bbt;
-               start = -1.0;
-       } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) {
-               error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
+
+       if ((prop = node.property ("beat")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+                       error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
+               } else {
+                       set_beat (beat);
+               }
+       } else {
+               error << _("TempoSection XML node has no \"beat\" property") << endmsg;
        }
 
-       set_start (start);
 
        if ((prop = node.property ("beats-per-minute")) == 0) {
                error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
@@ -132,13 +144,9 @@ TempoSection::TempoSection (const XMLNode& node)
        }
 
        if ((prop = node.property ("tempo-type")) == 0) {
-               _type = Type::Constant;
+               _type = Constant;
        } else {
-               if (strstr(prop->value().c_str(),"Constant")) {
-                       _type = Type::Constant;
-               } else {
-                       _type = Type::Ramp;
-               }
+               _type = Type (string_2_enum (prop->value(), _type));
        }
 }
 
@@ -150,7 +158,7 @@ TempoSection::get_state() const
        LocaleGuard lg;
 
        snprintf (buf, sizeof (buf), "%f", beat());
-       root->add_property ("start", buf);
+       root->add_property ("beat", buf);
        snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
        root->add_property ("beats-per-minute", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
@@ -159,9 +167,7 @@ TempoSection::get_state() const
        // root->add_property ("bar-offset", buf);
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
-
-       snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
-       root->add_property ("tempo-type", buf);
+       root->add_property ("tempo-type", enum_2_string (_type));
 
        return *root;
 }
@@ -170,10 +176,10 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = (start() * BBT_Time::ticks_per_beat) /
+       _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
                (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
 
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
+       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
 }
 
 void
@@ -182,6 +188,8 @@ TempoSection::set_type (Type type)
        _type = type;
 }
 
+/** returns the tempo at the zero-based (relative to tempo section) frame.
+*/
 double
 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
@@ -193,6 +201,9 @@ TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_f
        return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
 }
 
+/** returns the zero-based frame (relative to tempo section)
+   where the tempo occurs.
+*/
 framepos_t
 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
@@ -203,6 +214,10 @@ TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame
        return minute_to_frame (time_at_tick_tempo (tempo *  BBT_Time::ticks_per_beat,  end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
 }
 
+/** returns the zero-based tick (relative to tempo section)
+   where the zero-based frame (relative to tempo section)
+   lies.
+*/
 double
 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
@@ -213,6 +228,10 @@ TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_fr
        return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
 }
 
+/** returns the zero-based frame (relative to tempo section)
+   where the zero-based tick (relative to tempo section)
+   falls.
+*/
 framepos_t
 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
@@ -223,12 +242,23 @@ TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame,
        return minute_to_frame (time_at_tick (tick, end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
 }
 
-double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** returns the zero-based beat (relative to tempo section)
+   where the zero-based frame (relative to tempo section)
+   lies.
+*/
+double
+TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
        return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
 }
 
-framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** returns the zero-based frame (relative to tempo section start frame)
+   where the zero-based beat (relative to tempo section start)
+   falls.
+*/
+
+framepos_t
+TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
 {
        return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
 }
@@ -245,12 +275,14 @@ TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
        return (frame / (double) frame_rate) / 60.0;
 }
 
-/* constant for exp */
+/* position function */
 double
-TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
+TempoSection::a_func (double end_tpm, double c_func) const
 {
-       return log (end_tpm / ticks_per_minute()) /  c_func (end_tpm, end_time);
+       return log (end_tpm / ticks_per_minute()) /  c_func;
 }
+
+/*function constant*/
 double
 TempoSection::c_func (double end_tpm, double end_time) const
 {
@@ -271,20 +303,6 @@ TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_
        return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
 }
 
-/* tempo in bpm at time in minutes */
-double
-TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
-{
-       return tick_tempo_at_time (time, end_bpm *  BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
-}
-
-/* time in minutes at tempo in bpm */
-double
-TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
-{
-       return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
-}
-
 /* tick at time in minutes */
 double
 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
@@ -316,22 +334,22 @@ TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
 void
 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 {
-       double new_start;
+       double new_beat;
 
        if (_bar_offset < 0.0) {
                /* not set yet */
                return;
        }
 
-       new_start = start();
+       new_beat = beat();
 
        double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
-       new_start = ticks / BBT_Time::ticks_per_beat;
+       new_beat = ticks / BBT_Time::ticks_per_beat;
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
-                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start));
+                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
 
-       set_start (new_start);
+       set_beat (new_beat);
 }
 
 /***********************************************************************/
@@ -349,20 +367,28 @@ MeterSection::MeterSection (const XMLNode& node)
        double beat = 0.0;
        pair<double, BBT_Time> start;
 
-       if ((prop = node.property ("start")) == 0) {
-               error << _("MeterSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
-       }
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                    &bbt.bars,
                    &bbt.beats,
-                   &bbt.ticks) == 3) {
-               /* legacy session - start used to be in bbt*/
-               beat = -1.0;
-       } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
-               error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
-               throw failed_constructor();
+                   &bbt.ticks) < 3) {
+                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+               } else {
+                       /* legacy session - start used to be in bbt*/
+                       beat = -1.0;
+               }
+       } else {
+               error << _("MeterSection XML node has no \"start\" property") << endmsg;
+       }
+
+       if ((prop = node.property ("beat")) != 0) {
+               if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+                       error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
+               }
+       } else {
+               error << _("MeterSection XML node has no \"beat\" property") << endmsg;
        }
+
        start.first = beat;
 
        if ((prop = node.property ("bbt")) == 0) {
@@ -374,9 +400,10 @@ MeterSection::MeterSection (const XMLNode& node)
                error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
                throw failed_constructor();
        }
+
        start.second = bbt;
 
-       set_start (start);
+       set_beat (start);
 
        /* beats-per-bar is old; divisions-per-bar is new */
 
@@ -422,8 +449,8 @@ MeterSection::get_state() const
                  bbt().beats,
                  bbt().ticks);
        root->add_property ("bbt", buf);
-       snprintf (buf, sizeof (buf), "%f", start());
-       root->add_property ("start", buf);
+       snprintf (buf, sizeof (buf), "%lf", beat());
+       root->add_property ("beat", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
        snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
@@ -438,7 +465,7 @@ MeterSection::get_state() const
 
 struct MetricSectionSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->start() < b->start();
+           return a->beat() < b->beat();
     }
 };
 
@@ -457,7 +484,7 @@ TempoMap::TempoMap (framecnt_t fr)
        start.beats = 1;
        start.ticks = 0;
 
-       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
+       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
        MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        t->set_movable (false);
@@ -568,13 +595,13 @@ TempoMap::do_insert (MetricSection* section)
 
                if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
-                       pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
+                       pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
                        corrected.second.beats = 1;
                        corrected.second.ticks = 0;
-
+                       corrected.first = bbt_to_beats_locked (corrected.second);
                        warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
                                                   m->bbt(), corrected.second) << endmsg;
-                       m->set_start (corrected);
+                       m->set_beat (corrected);
                }
        }
 
@@ -596,7 +623,7 @@ TempoMap::do_insert (MetricSection* section)
 
                        /* Tempo sections */
 
-                       if (tempo->start() == insert_tempo->start()) {
+                       if (tempo->beat() == insert_tempo->beat()) {
 
                                if (!tempo->movable()) {
 
@@ -618,7 +645,7 @@ TempoMap::do_insert (MetricSection* section)
                        /* Meter Sections */
                        MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
                        MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
-                       if (meter->start() == insert_meter->start()) {
+                       if (meter->beat() == insert_meter->beat()) {
 
                                if (!meter->movable()) {
 
@@ -654,7 +681,7 @@ TempoMap::do_insert (MetricSection* section)
                        for (i = metrics.begin(); i != metrics.end(); ++i) {
                                MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
 
-                               if (meter && meter->start() > insert_meter->start()) {
+                               if (meter && meter->beat() > insert_meter->beat()) {
                                        break;
                                }
                        }
@@ -663,7 +690,7 @@ TempoMap::do_insert (MetricSection* section)
                                TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
 
                                if (tempo) {
-                                       if (tempo->start() > insert_tempo->start()) {
+                                       if (tempo->beat() > insert_tempo->beat()) {
                                                break;
                                        }
                                }
@@ -681,7 +708,7 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
 
-               if (ts.start() != first.start()) {
+               if (ts.beat() != first.beat()) {
                        remove_tempo_locked (ts);
                        add_tempo_locked (tempo, where, true, type);
                } else {
@@ -700,81 +727,77 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
 void
 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
 {
-       TempoSection& first (first_tempo());
-       if (ts.frame() != first.frame()) {
-               {
-                       Glib::Threads::RWLock::WriterLock lm (lock);
-                       /* currently this is always done in audio time */
-                       if (0) {
-                       //if (ts.position_lock_style() == AudioTime) {
-                               /* MusicTime */
-                               ts.set_start (beat_where);
-                               MetricSectionSorter cmp;
-                               metrics.sort (cmp);
-                       } else {
-                               /*AudioTime*/
-                               ts.set_frame (frame);
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
 
-                               MetricSectionFrameSorter fcmp;
-                               metrics.sort (fcmp);
+               /* currently this is always done in audio time */
+               //if (ts.position_lock_style() == MusicTime) {
+               if (0) {
+                       /* MusicTime */
+                       ts.set_beat (beat_where);
+                       MetricSectionSorter cmp;
+                       metrics.sort (cmp);
+               } else {
+                       /*AudioTime*/
+                       ts.set_frame (frame);
 
-                               Metrics::const_iterator i;
-                               TempoSection* prev_ts = 0;
-                               TempoSection* next_ts = 0;
+                       MetricSectionFrameSorter fcmp;
+                       metrics.sort (fcmp);
 
-                               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                                       TempoSection* t;
-                                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       Metrics::const_iterator i;
+                       TempoSection* prev_ts = 0;
+                       TempoSection* next_ts = 0;
 
-                                               if (t->frame() >= frame) {
-                                                       break;
-                                               }
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                                               prev_ts = t;
+                                       if (t->frame() >= frame) {
+                                               break;
                                        }
+
+                                       prev_ts = t;
                                }
+                       }
 
-                               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                                       TempoSection* t;
-                                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                                               if (t->frame() > frame) {
-                                                       next_ts = t;
-                                                       break;
-                                               }
+                                       if (t->frame() > frame) {
+                                               next_ts = t;
+                                               break;
                                        }
                                }
+                       }
 
-                               if (prev_ts) {
-                                       /* set the start beat */
-                                       double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
-                                       double beats = beats_to_ts + prev_ts->start();
-
-                                       if (next_ts) {
-                                               if (next_ts->start() < beats) {
-                                                       /* with frame-based editing, it is possible to get in a
-                                                          situation where if the tempo was placed at the mouse pointer frame,
-                                                          the following music-based tempo would jump to an earlier frame,
-                                                          changing the start beat of the moved tempo.
-                                                          in this case, we have to do some beat-based comparison TODO
-                                                       */
-
-                                                       ts.set_start (next_ts->start());
-                                               } else if (prev_ts->start() > beats) {
-                                                       ts.set_start (prev_ts->start());
-                                               } else {
-                                                       ts.set_start (beats);
-                                               }
+                       if (prev_ts) {
+                               /* set the start beat */
+                               double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
+                               double beats = beats_to_ts + prev_ts->beat();
+
+                               if (next_ts) {
+                                       if (next_ts->beat() < beats) {
+                                               /* with frame-based editing, it is possible to get in a
+                                                  situation where if the tempo was placed at the mouse pointer frame,
+                                                  the following music-based tempo would jump to an earlier frame,
+                                                  changing the beat beat of the moved tempo.
+                                                  in this case, we have to do some beat-based comparison TODO
+                                               */
+                                       } else if (prev_ts->beat() > beats) {
+                                               ts.set_beat (prev_ts->beat());
                                        } else {
-                                               ts.set_start (beats);
+                                               ts.set_beat (beats);
                                        }
-                                       MetricSectionSorter cmp;
-                                       metrics.sort (cmp);
+                               } else {
+                                       ts.set_beat (beats);
                                }
+                               MetricSectionSorter cmp;
+                               metrics.sort (cmp);
                        }
-
-                       recompute_map (false);
                }
+
+               recompute_map (false);
        }
 
        MetricPositionChanged (); // Emit Signal
@@ -805,14 +828,14 @@ TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, AR
 }
 
 void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                MeterSection& first (first_meter());
-               if (ms.start() != first.start()) {
+               if (ms.beat() != first.beat()) {
                        remove_meter_locked (ms);
-                       add_meter_locked (meter, start, where, true);
+                       add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
                } else {
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
@@ -824,11 +847,11 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const doubl
 }
 
 void
-TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, start, where, true);
+               add_meter_locked (meter, beat, where, true);
        }
 
 
@@ -842,7 +865,7 @@ TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
 }
 
 void
-TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
 {
        /* a new meter always starts a new bar on the first beat. so
           round the start time appropriately. remember that
@@ -859,7 +882,7 @@ TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bo
        /* new meters *always* start on a beat. */
        where.ticks = 0;
 
-       do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor()));
+       do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
 
        if (recompute) {
                recompute_map (true);
@@ -1008,12 +1031,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
-       MeterSection* meter = 0;
-       TempoSection* tempo = 0;
-       double current_frame;
-       BBT_Time current;
-       Metrics::iterator next_metric;
-
        if (end < 0) {
 
                /* we will actually stop once we hit
@@ -1025,122 +1042,59 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
 
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* ms;
-
-               if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
-                       meter = ms;
-                       break;
-               }
-       }
-
-       assert(meter);
-
-       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* ts;
-
-               if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
-                       tempo = ts;
-                       break;
-               }
-       }
-
-       assert(tempo);
-
-       /* assumes that the first meter & tempo are at frame zero */
-       current_frame = 0;
-       meter->set_frame (0);
-       tempo->set_frame (0);
-
-       /* assumes that the first meter & tempo are at 1|1|0 */
-       current.bars = 1;
-       current.beats = 1;
-       current.ticks = 0;
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
-
-       next_metric = metrics.begin();
-       ++next_metric; // skip meter (or tempo)
-       ++next_metric; // skip tempo (or meter)
-
-       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
-
        if (end == 0) {
                /* silly call from Session::process() during startup
                 */
                return;
        }
 
-       _extend_map (tempo, meter, next_metric, current, current_frame, end);
-}
-
-void
-TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
-                      Metrics::iterator next_metric,
-                      BBT_Time current, framepos_t current_frame, framepos_t end)
-{
-       /* CALLER MUST HOLD WRITE LOCK */
-
        Metrics::const_iterator i;
-       Metrics::const_iterator mi;
-
-       TempoSection* prev_ts = tempo;
-
-       for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
-               MeterSection* m = 0;
 
-               if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
-
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
+       TempoSection* prev_ts = 0;
 
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-                                       if (t->start() > prev_ts->start()) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                                               /*tempo section (t) lies in the previous meter */
-                                               double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
-                                               double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
+                       if (prev_ts) {
+                               double const beats_relative_to_prev_ts = t->beat() - prev_ts->beat();
+                               double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
 
-                                               /* assume (falsely) that the target tempo is constant */
-                                               double const t_fpb = t->frames_per_beat (_frame_rate);
-                                               double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
-                                               /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */ 
-                                               double length_estimate = beats_relative_to_prev_ts * av_fpb;
+                               /* assume (falsely) that the target tempo is constant */
+                               double const t_fpb = t->frames_per_beat (_frame_rate);
+                               double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
+                               /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
+                               double length_estimate = beats_relative_to_prev_ts * av_fpb;
 
-                                               if (prev_ts->type() == TempoSection::Type::Constant) {
-                                                       length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
-                                               }
+                               if (prev_ts->type() == TempoSection::Constant) {
+                                       length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
+                               }
 
-                                               double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
-                                               double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
+                               double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
+                               double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
 
-                                               while (fabs (tick_error) > system_precision_at_target_tempo) {
+                               while (fabs (tick_error) > system_precision_at_target_tempo) {
 
-                                                       double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
-                                                                                                           (framepos_t) length_estimate, _frame_rate);
-                                                       tick_error = ticks_relative_to_prev_ts - actual_ticks;
-                                                       length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
-                                               }
+                                       double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
+                                                                                           (framepos_t) length_estimate, _frame_rate);
+                                       tick_error = ticks_relative_to_prev_ts - actual_ticks;
+                                       length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
+                               }
 
-                                               t->set_frame (length_estimate + prev_ts->frame());
+                               t->set_frame (length_estimate + prev_ts->frame());
+                       }
+                       prev_ts = t;
+               }
+       }
 
-                                               double const meter_start_beats = m->start();
-                                               if (meter_start_beats < t->start() && meter_start_beats == prev_ts->start()) {
+       Metrics::const_iterator mi;
+       MeterSection* meter = 0;
 
-                                                       m->set_frame (prev_ts->frame());
-                                               } else if (meter_start_beats < t->start() && meter_start_beats > prev_ts->start()) {
-                                                       framepos_t new_frame = prev_ts->frame_at_beat (m->start() - prev_ts->start(),
-                                                                                                      t->beats_per_minute(),
-                                                                                                      t->frame() - prev_ts->frame(),
-                                                                                                      _frame_rate) + prev_ts->frame();
-                                                       m->set_frame (new_frame);
-                                               }
-                                       }
-                                       prev_ts = t;
-                               }
-                       }
-                       meter = m;
+       for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
+               /* we can do this beacuse we have the tempo section frames set */
+               if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
+                       meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
                }
        }
 }
@@ -1216,11 +1170,18 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
                warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
                return;
        }
-       bbt = beats_to_bbt (beat_at_frame (frame));
+       bbt = beats_to_bbt_locked (beat_at_frame (frame));
 }
 
 double
 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return bbt_to_beats_locked (bbt);
+}
+
+double
+TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
 {
        /* CALLER HOLDS READ LOCK */
 
@@ -1235,13 +1196,13 @@ TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
                        double bars_to_m = 0.0;
                        if (prev_ms) {
-                               bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
+                               bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
                        }
                        if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
                                break;
                        }
                        if (prev_ms) {
-                               accumulated_beats += m->start() - prev_ms->start();
+                               accumulated_beats += m->beat() - prev_ms->beat();
                                accumulated_bars += bars_to_m;
                        }
                        prev_ms = m;
@@ -1250,13 +1211,19 @@ TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
 
        double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
        double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
-       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.ticks / BBT_Time::ticks_per_beat);
-
+       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
        return ret;
 }
 
 Timecode::BBT_Time
 TempoMap::beats_to_bbt (double beats)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beats_to_bbt_locked (beats);
+}
+
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_locked (double beats)
 {
        /* CALLER HOLDS READ LOCK */
 
@@ -1270,21 +1237,21 @@ TempoMap::beats_to_bbt (double beats)
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                       if (beats < m->start()) {
+                       if (beats < m->beat()) {
                                /* this is the meter after the one our beat is on*/
                                break;
                        }
 
                        if (prev_ms) {
                                /* we need a whole number of bars. */
-                               accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
+                               accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
                        }
 
                        prev_ms = m;
                }
        }
 
-       double const beats_in_ms = beats - prev_ms->start();
+       double const beats_in_ms = beats - prev_ms->beat();
        uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
        uint32_t const total_bars = bars_in_ms + accumulated_bars;
        double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
@@ -1316,9 +1283,10 @@ TempoMap::beats_to_bbt (double beats)
 double
 TempoMap::tick_at_frame (framecnt_t frame) const
 {
+       /* HOLD (at least) THE READER LOCK */
 
        Metrics::const_iterator i;
-       const TempoSection* prev_ts = 0;
+       TempoSection* prev_ts = 0;
        double accumulated_ticks = 0.0;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
@@ -1329,27 +1297,24 @@ TempoMap::tick_at_frame (framecnt_t frame) const
                        if ((prev_ts) && frame < t->frame()) {
                                /*the previous ts is the one containing the frame */
 
-                               framepos_t time = frame - prev_ts->frame();
-                               framepos_t last_frame = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
+                               framepos_t const time = frame - prev_ts->frame();
+                               framepos_t const last_frame = t->frame() - prev_ts->frame();
+                               double const last_beats_per_minute = t->beats_per_minute();
 
                                return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
                        }
 
                        if (prev_ts && t->frame() > prev_ts->frame()) {
-                               framepos_t time = t->frame() - prev_ts->frame();
-                               framepos_t last_frame = t->frame() - prev_ts->frame();
-                               double last_beats_per_minute = t->beats_per_minute();
-                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
                        }
 
                        prev_ts = t;
                }
        }
 
-       /* treated s linear for this ts */
-       framecnt_t frames_in_section = frame - prev_ts->frame();
-       double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+       /* treated as constant for this ts */
+       framecnt_t const frames_in_section = frame - prev_ts->frame();
+       double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
 
        return ticks_in_section + accumulated_ticks;
 
@@ -1371,28 +1336,26 @@ TempoMap::frame_at_tick (double tick) const
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
                        if (prev_ts && t->frame() > prev_ts->frame()) {
-                               framepos_t const time = t->frame() - prev_ts->frame();
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
-                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
                        }
 
                        if (prev_ts && tick < accumulated_ticks) {
                                /* prev_ts is the one affecting us. */
 
                                double const ticks_in_section = tick - accumulated_ticks_to_prev;
-                               framepos_t const section_start = prev_ts->frame();
                                framepos_t const last_time = t->frame() - prev_ts->frame();
                                double const last_beats_per_minute = t->beats_per_minute();
 
-                               return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
+                               return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
                        }
                        accumulated_ticks_to_prev = accumulated_ticks;
                        prev_ts = t;
                }
        }
+       /* must be treated as constant, irrespective of _type */
        double const ticks_in_section = tick - accumulated_ticks_to_prev;
-       double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
+       double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
+
        framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
 
        return ret;
@@ -1427,7 +1390,7 @@ TempoMap::frame_time (const BBT_Time& bbt)
        }
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
+       framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
 
        return ret;
 }
@@ -1586,7 +1549,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 
        double const beat_at_framepos = beat_at_frame (frame);
 
-       BBT_Time bbt (beats_to_bbt (beat_at_framepos));
+       BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
 
        switch (type) {
        case Bar:
@@ -1646,7 +1609,7 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                framecnt_t const pos = frame_at_beat (cnt);
                MeterSection const meter = meter_section_at (pos);
                Tempo const tempo = tempo_at (pos);
-               BBT_Time const bbt = beats_to_bbt ((double) cnt);
+               BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
 
                points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
                ++cnt;
@@ -1855,15 +1818,15 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        MeterSection* prev_ms;
                        TempoSection* prev_ts;
                        if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               if (prev_ms->start() < 0.0) {
+                               if (prev_ms->beat() < 0.0) {
                                        /*XX we cannot possibly make this work??. */
                                        pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
-                                       prev_ms->set_start (start);
+                                       prev_ms->set_beat (start);
                                }
                        } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               if (prev_ts->start() < 0.0) {
+                               if (prev_ts->beat() < 0.0) {
                                        double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
-                                       prev_ts->set_start (start);
+                                       prev_ts->set_beat (start);
 
                                }
                        }
@@ -1880,15 +1843,15 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                                TempoSection* ts;
                                TempoSection* prev_ts;
                                if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                                       if (prev_ms->start() == ms->start()) {
-                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
-                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
+                                       if (prev_ms->beat() == ms->beat()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
                                                return -1;
                                        }
                                } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
-                                       if (prev_ts->start() == ts->start()) {
-                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
-                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
+                                       if (prev_ts->beat() == ts->beat()) {
+                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
+                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
                                                return -1;
                                        }
                                }
@@ -1914,7 +1877,7 @@ TempoMap::dump (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 << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
+                       o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
                          << t->movable() << ')' << endl;
                } 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()
@@ -1995,22 +1958,22 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        if (prev) {
                                if (ts){
                                        if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               ts->set_start (t->start());
+                                               ts->set_beat (t->beat());
                                        }
                                        if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               ts->set_start (m->start());
+                                               ts->set_beat (m->beat());
                                        }
                                        ts->set_frame (prev->frame());
 
                                }
                                if (ms) {
                                        if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
-                                               pair<double, BBT_Time> start = make_pair (m->start(), m->bbt());
-                                               ms->set_start (start);
+                                               pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
+                                               ms->set_beat (start);
                                        }
                                        if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
-                                               pair<double, BBT_Time> start = make_pair (t->start(), beats_to_bbt (t->start()));
-                                               ms->set_start (start);
+                                               pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
+                                               ms->set_beat (start);
                                        }
                                        ms->set_frame (prev->frame());
                                }
@@ -2023,9 +1986,9 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                        // cerr << bbt << endl;
 
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               t->set_start (beat_at_frame (m->frame()));
+                               t->set_beat (beat_at_frame (m->frame()));
                                tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
                        } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
                                bbt_time (m->frame(), bbt);
 
@@ -2049,9 +2012,9 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                        }
                                }
                                pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
-                               m->set_start (start);
+                               m->set_beat (start);
                                meter = m;
-                               // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+                               // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                abort(); /*NOTREACHED*/