Tempo ramps - api rename, fix various meter and tempo dialog bugs.
[ardour.git] / libs / ardour / tempo.cc
index d4ccffdd9e1cfecfde610e7e1658de58eec3b259..553f7ab1de34d763988eca4fc1c471bac438e9bd 100644 (file)
@@ -71,26 +71,38 @@ 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 (BBT_Time()), Tempo (TempoMap::default_tempo())
+       : MetricSection (0.0), Tempo (TempoMap::default_tempo())
 {
        const XMLProperty *prop;
-       BBT_Time start;
        LocaleGuard lg;
-
-       if ((prop = node.property ("start")) == 0) {
+       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 {
                error << _("TempoSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &start.bars,
-                   &start.beats,
-                   &start.ticks) < 3) {
-               error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
-               throw failed_constructor();
+
+       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;
@@ -146,11 +158,8 @@ TempoSection::get_state() const
        char buf[256];
        LocaleGuard lg;
 
-       snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 start().bars,
-                 start().beats,
-                 start().ticks);
-       root->add_property ("start", buf);
+       snprintf (buf, sizeof (buf), "%f", beat());
+       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);
@@ -170,10 +179,10 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
+       _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 +191,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 +204,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 +217,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 +231,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 +245,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 +278,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 +306,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,26 +337,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)
 {
-       BBT_Time new_start;
+       double new_beat;
 
        if (_bar_offset < 0.0) {
                /* not set yet */
                return;
        }
 
-       new_start.bars = start().bars;
+       new_beat = beat();
 
        double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
-       new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
-       //new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
-       new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
+       new_beat = ticks / BBT_Time::ticks_per_beat;
 
-       /* remember the 1-based counting properties of beats */
-       new_start.beats += 1;
        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.ticks, new_start.beats));
+                                                      _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
 
-       set_start (new_start);
+       set_beat (new_beat);
 }
 
 /***********************************************************************/
@@ -343,26 +360,53 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 const string MeterSection::xml_state_node_name = "Meter";
 
 MeterSection::MeterSection (const XMLNode& node)
-       : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
+       : MetricSection (0.0), Meter (TempoMap::default_meter())
 {
        XMLProperty const * prop;
        BBT_Time start;
        LocaleGuard lg;
-
-       if ((prop = node.property ("start")) == 0) {
+       const XMLProperty *prop;
+       BBT_Time bbt;
+       double beat = 0.0;
+       pair<double, BBT_Time> start;
+
+       if ((prop = node.property ("start")) != 0) {
+               if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+                       error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+               } else {
+                       /* legacy session - start used to be in bbt*/
+                       beat = -1.0;
+               }
+       } else {
                error << _("MeterSection XML node has no \"start\" property") << endmsg;
-               throw failed_constructor();
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                   &start.bars,
-                   &start.beats,
-                   &start.ticks) < 3) {
-               error << _("MeterSection 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 << _("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) {
+               error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
+       } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+                   &bbt.bars,
+                   &bbt.beats,
+                   &bbt.ticks) < 3) {
+               error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
                throw failed_constructor();
        }
 
-       set_start (start);
+       start.second = bbt;
+
+       set_beat (start);
 
        /* beats-per-bar is old; divisions-per-bar is new */
 
@@ -404,10 +448,12 @@ MeterSection::get_state() const
        LocaleGuard lg;
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
-                 start().bars,
-                 start().beats,
-                 start().ticks);
-       root->add_property ("start", buf);
+                 bbt().bars,
+                 bbt().beats,
+                 bbt().ticks);
+       root->add_property ("bbt", buf);
+       snprintf (buf, sizeof (buf), "%lf", beat());
+       root->add_property ("beat", buf);
        snprintf (buf, sizeof (buf), "%f", _note_type);
        root->add_property ("note-type", buf);
        snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
@@ -422,7 +468,13 @@ MeterSection::get_state() const
 
 struct MetricSectionSorter {
     bool operator() (const MetricSection* a, const MetricSection* b) {
-           return a->start() < b->start();
+           return a->beat() < b->beat();
+    }
+};
+
+struct MetricSectionFrameSorter {
+    bool operator() (const MetricSection* a, const MetricSection* b) {
+           return a->frame() < b->frame();
     }
 };
 
@@ -435,8 +487,8 @@ TempoMap::TempoMap (framecnt_t fr)
        start.beats = 1;
        start.ticks = 0;
 
-       TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
-       MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
+       TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Constant);
+       MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        t->set_movable (false);
        m->set_movable (false);
@@ -536,24 +588,23 @@ TempoMap::do_insert (MetricSection* section)
        /* we only allow new meters to be inserted on beat 1 of an existing
         * measure.
         */
-
-       if (dynamic_cast<MeterSection*>(section)) {
-               assert (section->start().ticks == 0);
+       MeterSection* m = 0;
+       if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
+               assert (m->bbt().ticks == 0);
 
                /* we need to (potentially) update the BBT times of tempo
                   sections based on this new meter.
                */
 
-               if ((section->start().beats != 1) || (section->start().ticks != 0)) {
-
-                       BBT_Time corrected = section->start();
-                       corrected.beats = 1;
-                       corrected.ticks = 0;
+               if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
+                       pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
+                       corrected.second.beats = 1;
+                       corrected.second.ticks = 0;
+                       corrected.first = bbt_to_beats_unlocked (corrected.second);
                        warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
-                                                  section->start(), corrected) << endmsg;
-
-                       section->set_start (corrected);
+                                                  m->bbt(), corrected.second) << endmsg;
+                       m->set_beat (corrected);
                }
        }
 
@@ -568,24 +619,23 @@ TempoMap::do_insert (MetricSection* section)
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
 
-               bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
-               bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
+               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+               TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
 
-               if (iter_is_tempo && insert_is_tempo) {
+               if (tempo && insert_tempo) {
 
                        /* Tempo sections */
 
-                       if ((*i)->start().bars == section->start().bars &&
-                           (*i)->start().beats == section->start().beats) {
+                       if (tempo->beat() == insert_tempo->beat()) {
 
-                               if (!(*i)->movable()) {
+                               if (!tempo->movable()) {
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section).
                                         */
 
-                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
+                                       *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
@@ -593,20 +643,21 @@ TempoMap::do_insert (MetricSection* section)
                                break;
                        }
 
-               } else if (!iter_is_tempo && !insert_is_tempo) {
+               } else if (!tempo && !insert_tempo) {
 
                        /* Meter Sections */
+                       MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+                       MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+                       if (meter->beat() == insert_meter->beat()) {
 
-                       if ((*i)->start().bars == section->start().bars) {
-
-                               if (!(*i)->movable()) {
+                               if (!meter->movable()) {
 
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section
                                         */
 
-                                       *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
+                                       *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
@@ -625,12 +676,27 @@ TempoMap::do_insert (MetricSection* section)
         */
 
        if (need_add) {
+               MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+               TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
 
                Metrics::iterator i;
+               if (insert_meter) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
 
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       if ((*i)->start() > section->start()) {
-                               break;
+                               if (meter && meter->beat() > insert_meter->beat()) {
+                                       break;
+                               }
+                       }
+               } else if (insert_tempo) {
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+
+                               if (tempo) {
+                                       if (tempo->beat() > insert_tempo->beat()) {
+                                               break;
+                                       }
+                               }
                        }
                }
 
@@ -639,13 +705,13 @@ TempoMap::do_insert (MetricSection* section)
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::Type type)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
 {
        {
                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 {
@@ -662,29 +728,86 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T
 }
 
 void
-TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame)
+TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
 {
        {
-               TempoSection& first (first_tempo());
+               Glib::Threads::RWLock::WriterLock lm (lock);
 
-               if (ts.start() != first.start()) {
-                       BBT_Time bbt;
-                       bbt_time (frame, bbt);
-                       {
-                               Glib::Threads::RWLock::WriterLock lm (lock);
-                               ts.set_frame (frame);
-                               ts.set_start (bbt);
+               /* 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);
 
-                               recompute_map (false);
+                       MetricSectionFrameSorter fcmp;
+                       metrics.sort (fcmp);
+
+                       Metrics::const_iterator i;
+                       TempoSection* prev_ts = 0;
+                       TempoSection* next_ts = 0;
+
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                                       if (t->frame() >= frame) {
+                                               break;
+                                       }
+
+                                       prev_ts = t;
+                               }
+                       }
+
+                       for (i = metrics.begin(); i != metrics.end(); ++i) {
+                               TempoSection* t;
+                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                                       if (t->frame() > frame) {
+                                               next_ts = t;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (prev_ts) {
+                               /* set the start beat */
+                               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_beat (beats);
+                                       }
+                               } else {
+                                       ts.set_beat (beats);
+                               }
+                               MetricSectionSorter cmp;
+                               metrics.sort (cmp);
                        }
                }
+
+               recompute_map (false);
        }
 
        MetricPositionChanged (); // Emit Signal
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::Type type)
+TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
@@ -696,40 +819,10 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::T
 }
 
 void
-TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::Type type)
+TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
 {
        TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
 
-       /* find the meter to use to set the bar offset of this
-        * tempo section.
-        */
-
-       const Meter* meter = &first_meter();
-
-       /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
-          at something, because we insert the default tempo and meter during
-          TempoMap construction.
-
-          now see if we can find better candidates.
-       */
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-
-               const MeterSection* m;
-
-               if (where < (*i)->start()) {
-                       break;
-               }
-
-               if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       meter = m;
-               }
-       }
-
-       ts->update_bar_offset_from_bbt (*meter);
-
-       /* and insert it */
-
        do_insert (ts);
 
        if (recompute) {
@@ -743,10 +836,9 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
        {
                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, where, true);
+                       add_meter_locked (meter, bbt_to_beats_unlocked (where), where, true);
                } else {
                        /* cannot move the first meter section */
                        *static_cast<Meter*>(&first) = meter;
@@ -758,11 +850,11 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
 }
 
 void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_meter_locked (meter, where, true);
+               add_meter_locked (meter, beat, where, true);
        }
 
 
@@ -776,7 +868,7 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
 }
 
 void
-TempoMap::add_meter_locked (const Meter& meter, 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
@@ -793,7 +885,7 @@ TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
        /* new meters *always* start on a beat. */
        where.ticks = 0;
 
-       do_insert (new MeterSection (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);
@@ -942,12 +1034,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
@@ -957,155 +1043,61 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 
        }
 
-       MetricSectionSorter cmp;
-       metrics.sort (cmp);
-
        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;
-               }
+       if (end == 0) {
+               /* silly call from Session::process() during startup
+                */
+               return;
        }
 
-       assert(tempo);
+       Metrics::const_iterator i;
 
-       /* assumes that the first meter & tempo are at frame zero */
-       current_frame = 0;
-       meter->set_frame (0);
-       tempo->set_frame (0);
+       TempoSection* prev_ts = 0;
 
-       /* assumes that the first meter & tempo are at 1|1|0 */
-       current.bars = 1;
-       current.beats = 1;
-       current.ticks = 0;
-       if (reassign_tempo_bbt) {
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-               MeterSection* rmeter = meter;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-               DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
+                       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;
 
-               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                               /* 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;
 
-                       TempoSection* ts;
-                       MeterSection* ms;
+                               if (prev_ts->type() == TempoSection::Type::Constant) {
+                                       length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
+                               }
 
-                       if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               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
 
-                               /* reassign the BBT time of this tempo section
-                                * based on its bar offset position.
-                                */
+                               while (fabs (tick_error) > system_precision_at_target_tempo) {
 
-                               ts->update_bbt_time_from_bar_offset (*rmeter);
+                                       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);
+                               }
 
-                       } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
-                               rmeter = ms;
-                       } else {
-                               fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               abort(); /*NOTREACHED*/
+                               t->set_frame (length_estimate + prev_ts->frame());
                        }
+                       prev_ts = t;
                }
        }
 
-       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 */
-
-       uint32_t first_tick_in_new_meter = 0;
-       Metrics::const_iterator i;
        Metrics::const_iterator mi;
-
-       TempoSection* prev_ts = tempo;
+       MeterSection* meter = 0;
 
        for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
-               MeterSection* m = 0;
-
-               if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
-
-                       if (m->start() >= prev_ts->start()) {
-                               first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter
-                               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                                       TempoSection* t;
-
-                                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                                               if (t->start() >= m->start() && t->start() > prev_ts->start()) {
-                                                       //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
-                                                       //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
-
-                                                       /*tempo section (t) lies in the previous meter */
-                                                       double const ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) )  * BBT_Time::ticks_per_beat) + t->start().ticks;
-                                                       double const ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1))  * BBT_Time::ticks_per_beat) + prev_ts->start().ticks;
-                                                       double const ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
-                                                       /* assume (falsely) that the target tempo is constant */
-                                                       double length_estimate = (ticks_relative_to_prev_ts /  BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
-                                                       if (prev_ts->type() == TempoSection::Type::Constant) {
-                                                               length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
-                                                       }
-                                                       cerr<< "initial length extimate = " << length_estimate << " ticks_relative_to_prev_ts " << ticks_relative_to_prev_ts << endl;
-                                                       double const system_precision_at_target_tempo =  (_frame_rate / t->ticks_per_minute());
-                                                       cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
-                                                       double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
-
-                                                       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 / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
-                                                               //cerr << "actual ticks = " << actual_ticks << endl;
-
-                                                               //cerr << "tick error  = " << tick_error << endl;
-                                                       }
-                                                       cerr << "setting t frame to " << length_estimate + prev_ts->frame() << " tick error  = " << tick_error << endl;
-                                                       t->set_frame (length_estimate + prev_ts->frame());
-
-                                                       if (m->start() < t->start() && m->start() == prev_ts->start()) {
-                                                               m->set_frame (prev_ts->frame());
-                                                       } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
-                                                               cerr << "recompute map - setting meter frame to " << prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) << " ticks = " << first_tick_in_new_meter - ticks_at_prev_ts  << endl;
-
-                                                               m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate));
-                                                       }
-                                               }
-                                               prev_ts = t;
-                                       }
-                               }
-                       }
-                       meter = m;
+               /* 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));
                }
        }
 }
@@ -1139,7 +1131,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 
        return m;
 }
-
+/* XX meters only */
 TempoMetric
 TempoMap::metric_at (BBT_Time bbt) const
 {
@@ -1154,14 +1146,16 @@ TempoMap::metric_at (BBT_Time bbt) const
        */
 
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* mw;
+               if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       BBT_Time section_start (mw->bbt());
 
-               BBT_Time section_start ((*i)->start());
+                       if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+                               break;
+                       }
 
-               if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
-                       break;
+                       m.set_metric (*i);
                }
-
-               m.set_metric (*i);
        }
 
        return m;
@@ -1179,161 +1173,109 @@ 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_unlocked (beat_at_frame (frame));
 }
 
-int32_t
-TempoMap::bars_in_meter_section (MeterSection* ms) const
+double
+TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
 {
-       /* YOU MUST HAVE THE READ LOCK */
-       Metrics::const_iterator i;
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return bbt_to_beats_unlocked (bbt);
+}
 
-       MeterSection* next_ms = 0;
-       const MeterSection* prev_ms = &first_meter();
+double
+TempoMap::bbt_to_beats_unlocked (Timecode::BBT_Time bbt)
+{
+       /* CALLER HOLDS READ LOCK */
+
+       double accumulated_beats = 0.0;
+       double accumulated_bars = 0.0;
+       MeterSection* prev_ms = 0;
+
+       Metrics::const_iterator i;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (ms->frame() < m->frame()) {
-                               next_ms = m;
+                       double bars_to_m = 0.0;
+                       if (prev_ms) {
+                               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->beat() - prev_ms->beat();
+                               accumulated_bars += bars_to_m;
+                       }
                        prev_ms = m;
                }
        }
-       if (next_ms) {
-               double ticks_at_next = tick_at_frame (next_ms->frame());
-               double ticks_at_prev = tick_at_frame (prev_ms->frame());
-               double ticks_in_meter = ticks_at_next - ticks_at_prev;
 
-               return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
-       }
-       return -1;
+       double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
+       double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
+       double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+       return ret;
 }
 
 Timecode::BBT_Time
 TempoMap::beats_to_bbt (double beats)
 {
-       /* CALLER HOLDS READ LOCK */
-       BBT_Time ret;
-       MeterSection* prev_ms = &first_meter();
-
-       framecnt_t frame = frame_at_beat (beats);
-       uint32_t cnt = 0;
-       /* XX most of this is utter crap */
-       if (n_meters() == 1) {
-               uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
-               double remaining_beats = beats - (bars *  prev_ms->note_divisor());
-               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
-
-               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
-               ret.beats = (uint32_t) floor (remaining_beats);
-               ret.bars = bars;
-
-               /* 0 0 0 to 1 1 0 - based mapping*/
-               ++ret.bars;
-               ++ret.beats;
-
-               if (ret.ticks >= BBT_Time::ticks_per_beat) {
-                       ++ret.beats;
-                       ret.ticks -= BBT_Time::ticks_per_beat;
-               }
-
-               if (ret.beats > prev_ms->note_divisor()) {
-                       ++ret.bars;
-                       ret.beats = 1;
-               }
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       return beats_to_bbt_unlocked (beats);
+}
 
-               return ret;
-       }
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_unlocked (double beats)
+{
+       /* CALLER HOLDS READ LOCK */
 
-       uint32_t first_beat_in_meter = 0;
+       MeterSection* prev_ms = 0;
        uint32_t accumulated_bars = 0;
+
        Metrics::const_iterator i;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
                MeterSection* m = 0;
 
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       first_beat_in_meter = beat_at_frame (m->frame());
 
-                       if (beats < first_beat_in_meter) {
+                       if (beats < m->beat()) {
                                /* this is the meter after the one our beat is on*/
                                break;
                        }
-                       int32_t const bars_in_ms = bars_in_meter_section (m);
 
-                       if (bars_in_ms > 0) {
-                               accumulated_bars += bars_in_ms;
+                       if (prev_ms) {
+                               /* we need a whole number of bars. */
+                               accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
                        }
 
                        prev_ms = m;
-                       ++cnt;
-               }
-       }
-       //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter =  " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars <<  endl;
-
-       if (beats > first_beat_in_meter) {
-               /* prev_ms is the relevant one here */
-
-               /* now get the ticks at frame */
-               double ticks_at_frame = tick_at_frame (frame);
-
-               /* find the number of ticks at the beginning of the meter section (bar 1)*/
-               double ticks_at_ms = tick_at_frame (prev_ms->frame());
-
-               double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
-
-               uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
-               double remaining_beats = beats_used_by_ms - (bars *  prev_ms->note_divisor());
-               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
-
-               ret.bars = bars + accumulated_bars;
-               ret.beats = (uint32_t) floor (remaining_beats);
-               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
-
-               /* now ensure we srtart at 1 1 0 */
-               ++ret.bars;
-               ++ret.beats;
-               //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
-               if (ret.ticks >= BBT_Time::ticks_per_beat) {
-                       ++ret.beats;
-                       ret.ticks -= BBT_Time::ticks_per_beat;
-               }
-
-               if (ret.beats > prev_ms->note_divisor()) {
-                       ++ret.bars;
-                       ret.beats = 1;
                }
-
-               return ret;
        }
 
-       /* find the number of ticks at the beginning of the meter section (bar 1)*/
-       double ticks_at_ms = tick_at_frame (prev_ms->frame());
-
-       /* now get the ticks at frame */
-       double ticks_at_frame = tick_at_frame (frame);
-
-       double ticks_within_ms = ticks_at_frame - ticks_at_ms;
+       double const beats_in_ms = beats - prev_ms->beat();
+       uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
+       uint32_t const total_bars = bars_in_ms + accumulated_bars;
+       double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
+       double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
-       ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
-       uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
-       ret.beats = (uint32_t) floor (remaining_ticks);
-       remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
+       BBT_Time ret;
 
-       /* only round ticks */
        ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+       ret.beats = (uint32_t) floor (remaining_beats);
+       ret.bars = total_bars;
 
-       /* now ensure we srtart at 1 1 0 */
+       /* 0 0 0 to 1 1 0 - based mapping*/
        ++ret.bars;
        ++ret.beats;
+
        if (ret.ticks >= BBT_Time::ticks_per_beat) {
                ++ret.beats;
                ret.ticks -= BBT_Time::ticks_per_beat;
        }
 
-       if (ret.beats > prev_ms->note_divisor()) {
+       if (ret.beats > prev_ms->divisions_per_bar()) {
                ++ret.bars;
                ret.beats = 1;
        }
@@ -1344,43 +1286,38 @@ TempoMap::beats_to_bbt (double beats)
 double
 TempoMap::tick_at_frame (framecnt_t frame) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       /* HOLD (at least) THE READER LOCK */
 
        Metrics::const_iterator i;
-       const TempoSection* prev_ts = &first_tempo();
+       TempoSection* prev_ts = 0;
        double accumulated_ticks = 0.0;
-       uint32_t cnt = 0;
 
        for (i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* t;
 
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                       if (frame < t->frame()) {
+                       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 (cnt > 0 && 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);
+                       if (prev_ts && t->frame() > prev_ts->frame()) {
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
                        }
 
                        prev_ts = t;
-                       ++cnt;
                }
        }
 
-       /* 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;
 
@@ -1393,9 +1330,7 @@ TempoMap::frame_at_tick (double tick) const
 
        double accumulated_ticks = 0.0;
        double accumulated_ticks_to_prev = 0.0;
-
-       const TempoSection* prev_ts =  &first_tempo();
-       uint32_t cnt = 0;
+       const TempoSection* prev_ts = 0;
 
        Metrics::const_iterator i;
 
@@ -1403,31 +1338,28 @@ TempoMap::frame_at_tick (double tick) const
                TempoSection* t;
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
-                               framepos_t time = t->frame() - prev_ts->frame();
-                               framepos_t last_time = 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_time, _frame_rate);
+                       if (prev_ts && t->frame() > prev_ts->frame()) {
+                               accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
                        }
 
-                       if (tick < accumulated_ticks) {
+                       if (prev_ts && tick < accumulated_ticks) {
                                /* prev_ts is the one affecting us. */
 
-                               double ticks_in_section = tick - accumulated_ticks_to_prev;
-                               framepos_t section_start = prev_ts->frame();
-                               framepos_t last_time = t->frame() - prev_ts->frame();
-                               double 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;
+                               double const ticks_in_section = tick - accumulated_ticks_to_prev;
+                               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) + prev_ts->frame();
                        }
                        accumulated_ticks_to_prev = accumulated_ticks;
-
                        prev_ts = t;
-                       ++cnt;
                }
        }
-       double ticks_in_section = tick - accumulated_ticks_to_prev;
-       double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
-       framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+       /* 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);
+
+       framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
 
        return ret;
 }
@@ -1459,73 +1391,10 @@ TempoMap::frame_time (const BBT_Time& bbt)
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
-       Metrics::const_iterator i;
-       uint32_t accumulated_bars = 0;
-
-       MeterSection* prev_ms = &first_meter();
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       int32_t const bims = bars_in_meter_section (m);
-
-                       if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
-                               break;
-                       }
-                       if (bims > 0 ) {
-                               accumulated_bars += bims;
-                       }
-                       prev_ms = m;
-               }
-       }
-
-       uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
-       double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
-       double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
-       double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
-
-       TempoSection* prev_ts = &first_tempo();
-       double accumulated_ticks = 0.0;
-       double accumulated_ticks_to_prev = 0.0;
-
-       uint32_t cnt = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       if (t->frame() < prev_ms->frame()) {
-                               continue;
-                       }
+       framepos_t const ret = frame_at_beat (bbt_to_beats_unlocked (bbt));
 
-                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
-                               /*find the number of ticke in this section */
-                               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);
-                       }
-
-                       if (ticks_target < accumulated_ticks) {
-                               double const ticks_in_section = ticks_target - accumulated_ticks_to_prev;
-                               framepos_t const section_start_time = prev_ts->frame();
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
-                               framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
-                               return ret;
-                       }
-                       accumulated_ticks_to_prev = accumulated_ticks;
-                       prev_ts = t;
-                       ++cnt;
-               }
-       }
-
-       /*treat this ts as constant tempo */
-       double const ticks_in_this_ts = ticks_target - accumulated_ticks_to_prev;
-       double const dtime = (ticks_in_this_ts / 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;
 }
 
@@ -1683,7 +1552,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_unlocked (beat_at_framepos));
 
        switch (type) {
        case Bar:
@@ -1743,37 +1612,13 @@ 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_unlocked ((double) cnt);
 
                points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
                ++cnt;
        }
 }
 
-TempoSection*
-TempoMap::tempo_section_after (framepos_t frame) const
-{
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-
-       Metrics::const_iterator i;
-       TempoSection* next = 0;
-
-       for (i = metrics.begin(); i != metrics.end(); ++i) {
-               TempoSection* t;
-
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
-                       if ((*i)->frame() > frame) {
-                               next = t;
-                               break;
-                       }
-               }
-       }
-
-       return next;
-}
-
-
 const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
@@ -1812,7 +1657,20 @@ TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        const TempoSection* ts_at = &tempo_section_at (frame);
-       const TempoSection* ts_after = tempo_section_after (frame);
+       const TempoSection* ts_after = 0;
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if ((*i)->frame() > frame) {
+                               ts_after = t;
+                               break;
+                       }
+               }
+       }
 
        if (ts_after) {
                return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
@@ -1927,7 +1785,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
 
                                        if (ts->bar_offset() < 0.0) {
                                                if (last_meter) {
-                                                       ts->update_bar_offset_from_bbt (*last_meter);
+                                                       //ts->update_bar_offset_from_bbt (*last_meter);
                                                }
                                        }
                                }
@@ -1958,7 +1816,24 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        MetricSectionSorter cmp;
                        metrics.sort (cmp);
                }
+               /* check for legacy sessions where bbt was the base musical unit for tempo */
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       MeterSection* prev_ms;
+                       TempoSection* prev_ts;
+                       if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               if (prev_ms->beat() < 0.0) {
+                                       /*XX we cannot possibly make this work??. */
+                                       pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
+                                       prev_ms->set_beat (start);
+                               }
+                       } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               if (prev_ts->beat() < 0.0) {
+                                       double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
+                                       prev_ts->set_beat (start);
 
+                               }
+                       }
+               }
                /* check for multiple tempo/meters at the same location, which
                   ardour2 somehow allowed.
                */
@@ -1966,16 +1841,20 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                Metrics::iterator prev = metrics.end();
                for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                        if (prev != metrics.end()) {
-                               if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
-                                               error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
+                               MeterSection* ms;
+                               MeterSection* prev_ms;
+                               TempoSection* ts;
+                               TempoSection* prev_ts;
+                               if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (prev_ms->beat() == ms->beat()) {
+                                               cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
+                                               error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
                                                return -1;
                                        }
-                               } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
-                                       if ((*prev)->start() == (*i)->start()) {
-                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
-                                               error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
+                               } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+                                       if (prev_ts->beat() == ts->beat()) {
+                                               cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
+                                               error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
                                                return -1;
                                        }
                                }
@@ -2001,10 +1880,10 @@ 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->start() << " frame= " << m->frame()
+                       o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
                          << " (movable? " << m->movable() << ')' << endl;
                }
        }
@@ -2076,48 +1955,69 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                for (i = metrics.begin(); i != metrics.end(); ++i) {
 
                        BBT_Time bbt;
-                       TempoMetric metric (*meter, *tempo);
-
+                       //TempoMetric metric (*meter, *tempo);
+                       MeterSection* ms = const_cast<MeterSection*>(meter);
+                       TempoSection* ts = const_cast<TempoSection*>(tempo);
                        if (prev) {
-                               metric.set_start (prev->start());
-                               metric.set_frame (prev->frame());
+                               if (ts){
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               ts->set_beat (t->beat());
+                                       }
+                                       if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+                                               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->beat(), m->bbt());
+                                               ms->set_beat (start);
+                                       }
+                                       if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+                                               pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_unlocked (t->beat()));
+                                               ms->set_beat (start);
+                                       }
+                                       ms->set_frame (prev->frame());
+                               }
+
                        } else {
                                // metric will be at frames=0 bbt=1|1|0 by default
                                // which is correct for our purpose
                        }
 
-                       bbt_time ((*i)->frame(), bbt);
-
-                       // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
-
-                       if (first) {
-                               first = false;
-                       } else {
+                       // cerr << bbt << endl;
 
-                               if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
-                                       /* round up to next beat */
-                                       bbt.beats += 1;
-                               }
+                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+                               t->set_beat (beat_at_frame (m->frame()));
+                               tempo = t;
+                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                               bbt_time (m->frame(), bbt);
 
-                               bbt.ticks = 0;
+                               // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
 
-                               if (bbt.beats != 1) {
-                                       /* round up to next bar */
-                                       bbt.bars += 1;
-                                       bbt.beats = 1;
-                               }
-                       }
+                               if (first) {
+                                       first = false;
+                               } else {
 
-                       // cerr << bbt << endl;
+                                       if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
+                                               /* round up to next beat */
+                                               bbt.beats += 1;
+                                       }
 
-                       (*i)->set_start (bbt);
+                                       bbt.ticks = 0;
 
-                       if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
-                               tempo = t;
-                               // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
-                       } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+                                       if (bbt.beats != 1) {
+                                               /* round up to next bar */
+                                               bbt.bars += 1;
+                                               bbt.beats = 1;
+                                       }
+                               }
+                               pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
+                               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*/
@@ -2406,7 +2306,7 @@ operator<< (std::ostream& o, const Tempo& t) {
 std::ostream&
 operator<< (std::ostream& o, const MetricSection& section) {
 
-       o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
+       o << "MetricSection @ " << section.frame() << ' ';
 
        const TempoSection* ts;
        const MeterSection* ms;
@@ -2414,7 +2314,7 @@ operator<< (std::ostream& o, const MetricSection& section) {
        if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
                o << *((const Tempo*) ts);
        } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
-               o << *((const Meter*) ms);
+               //o << *((const Meter*) ms);
        }
 
        return o;