Add loudness normalization to Export Format & Graph
[ardour.git] / libs / ardour / tempo.cc
index 80091e7eec01ed90b35514e40a023c9b444d5977..2a0ac50cc2759d31ce19b1caf5f4f84122b87ec2 100644 (file)
@@ -25,8 +25,9 @@
 
 #include <glibmm/threads.h>
 #include "pbd/xml++.h"
 
 #include <glibmm/threads.h>
 #include "pbd/xml++.h"
-#include "evoral/types.hpp"
+#include "evoral/Beats.hpp"
 #include "ardour/debug.h"
 #include "ardour/debug.h"
+#include "ardour/lmath.h"
 #include "ardour/tempo.h"
 
 #include "i18n.h"
 #include "ardour/tempo.h"
 
 #include "i18n.h"
@@ -45,11 +46,11 @@ Tempo    TempoMap::_default_tempo (120.0);
 
 /***********************************************************************/
 
 
 /***********************************************************************/
 
-double 
+double
 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
 {
        /* This is tempo- and meter-sensitive. The number it returns
 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
 {
        /* This is tempo- and meter-sensitive. The number it returns
-          is based on the interval between any two lines in the 
+          is based on the interval between any two lines in the
           grid that is constructed from tempo and meter sections.
 
           The return value IS NOT interpretable in terms of "beats".
           grid that is constructed from tempo and meter sections.
 
           The return value IS NOT interpretable in terms of "beats".
@@ -73,7 +74,7 @@ TempoSection::TempoSection (const XMLNode& node)
 {
        const XMLProperty *prop;
        BBT_Time start;
 {
        const XMLProperty *prop;
        BBT_Time start;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        if ((prop = node.property ("start")) == 0) {
                error << _("TempoSection XML node has no \"start\" property") << endmsg;
 
        if ((prop = node.property ("start")) == 0) {
                error << _("TempoSection XML node has no \"start\" property") << endmsg;
@@ -132,7 +133,7 @@ TempoSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  start().bars,
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  start().bars,
@@ -155,7 +156,7 @@ void
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
 
 TempoSection::update_bar_offset_from_bbt (const Meter& m)
 {
-       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / 
+       _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
                (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()));
                (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()));
@@ -172,15 +173,15 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
        }
 
        new_start.bars = start().bars;
        }
 
        new_start.bars = start().bars;
-       
+
        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); */
 
        /* remember the 1-based counting properties of beats */
        new_start.beats += 1;
        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); */
 
        /* 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", 
+
+       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));
 
        set_start (new_start);
                                                       _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
 
        set_start (new_start);
@@ -195,7 +196,7 @@ MeterSection::MeterSection (const XMLNode& node)
 {
        const XMLProperty *prop;
        BBT_Time start;
 {
        const XMLProperty *prop;
        BBT_Time start;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        if ((prop = node.property ("start")) == 0) {
                error << _("MeterSection XML node has no \"start\" property") << endmsg;
 
        if ((prop = node.property ("start")) == 0) {
                error << _("MeterSection XML node has no \"start\" property") << endmsg;
@@ -218,7 +219,7 @@ MeterSection::MeterSection (const XMLNode& node)
                if ((prop = node.property ("beats-per-bar")) == 0) {
                        error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
                        throw failed_constructor();
                if ((prop = node.property ("beats-per-bar")) == 0) {
                        error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
                        throw failed_constructor();
-               } 
+               }
        }
 
        if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
        }
 
        if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
@@ -249,7 +250,7 @@ MeterSection::get_state() const
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
 {
        XMLNode *root = new XMLNode (xml_state_node_name);
        char buf[256];
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  start().bars,
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  start().bars,
@@ -283,6 +284,7 @@ TempoMap::TempoMap (framecnt_t fr)
        start.beats = 1;
        start.ticks = 0;
 
        start.beats = 1;
        start.ticks = 0;
 
+       // these leak memory, well Metrics does
        TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
        MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
        MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
@@ -306,23 +308,11 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               Metrics::iterator i;
-
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       if (dynamic_cast<TempoSection*> (*i) != 0) {
-                               if (tempo.frame() == (*i)->frame()) {
-                                       if ((*i)->movable()) {
-                                               metrics.erase (i);
-                                               removed = true;
-                                               break;
-                                       }
-                               }
+               if ((removed = remove_tempo_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
                        }
                }
-
-               if (removed && complete_operation) {
-                       recompute_map (false);
-               }
        }
 
        if (removed && complete_operation) {
        }
 
        if (removed && complete_operation) {
@@ -330,6 +320,25 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
        }
 }
 
        }
 }
 
+bool
+TempoMap::remove_tempo_locked (const TempoSection& tempo)
+{
+       Metrics::iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               if (dynamic_cast<TempoSection*> (*i) != 0) {
+                       if (tempo.frame() == (*i)->frame()) {
+                               if ((*i)->movable()) {
+                                       metrics.erase (i);
+                                       return true;
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
 void
 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 {
 void
 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 {
@@ -337,23 +346,11 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               Metrics::iterator i;
-
-               for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       if (dynamic_cast<MeterSection*> (*i) != 0) {
-                               if (tempo.frame() == (*i)->frame()) {
-                                       if ((*i)->movable()) {
-                                               metrics.erase (i);
-                                               removed = true;
-                                               break;
-                                       }
-                               }
+               if ((removed = remove_meter_locked (tempo))) {
+                       if (complete_operation) {
+                               recompute_map (true);
                        }
                }
                        }
                }
-
-               if (removed && complete_operation) {
-                       recompute_map (true);
-               }
        }
 
        if (removed && complete_operation) {
        }
 
        if (removed && complete_operation) {
@@ -361,6 +358,25 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
        }
 }
 
        }
 }
 
+bool
+TempoMap::remove_meter_locked (const MeterSection& tempo)
+{
+       Metrics::iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               if (dynamic_cast<MeterSection*> (*i) != 0) {
+                       if (tempo.frame() == (*i)->frame()) {
+                               if ((*i)->movable()) {
+                                       metrics.erase (i);
+                                       return true;
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
 void
 TempoMap::do_insert (MetricSection* section)
 {
 void
 TempoMap::do_insert (MetricSection* section)
 {
@@ -369,7 +385,7 @@ TempoMap::do_insert (MetricSection* section)
        assert (section->start().ticks == 0);
 
        /* we only allow new meters to be inserted on beat 1 of an existing
        assert (section->start().ticks == 0);
 
        /* we only allow new meters to be inserted on beat 1 of an existing
-        * measure. 
+        * measure.
         */
 
        if (dynamic_cast<MeterSection*>(section)) {
         */
 
        if (dynamic_cast<MeterSection*>(section)) {
@@ -377,21 +393,21 @@ TempoMap::do_insert (MetricSection* section)
                /* we need to (potentially) update the BBT times of tempo
                   sections based on this new meter.
                */
                /* 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)) {
                if ((section->start().beats != 1) || (section->start().ticks != 0)) {
-                       
+
                        BBT_Time corrected = section->start();
                        corrected.beats = 1;
                        corrected.ticks = 0;
                        BBT_Time corrected = section->start();
                        corrected.beats = 1;
                        corrected.ticks = 0;
-                       
+
                        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;
                        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);
                }
        }
 
                        section->set_start (corrected);
                }
        }
 
-       
+
 
        /* Look for any existing MetricSection that is of the same type and
           in the same bar as the new one, and remove it before adding
 
        /* Look for any existing MetricSection that is of the same type and
           in the same bar as the new one, and remove it before adding
@@ -413,19 +429,19 @@ TempoMap::do_insert (MetricSection* section)
                            (*i)->start().beats == section->start().beats) {
 
                                if (!(*i)->movable()) {
                            (*i)->start().beats == section->start().beats) {
 
                                if (!(*i)->movable()) {
-                                       
+
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section).
                                         */
                                        /* 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));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
                                }
                                break;
                                        *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
                                }
                                break;
-                       } 
+                       }
 
                } else if (!iter_is_tempo && !insert_is_tempo) {
 
 
                } else if (!iter_is_tempo && !insert_is_tempo) {
 
@@ -434,17 +450,17 @@ TempoMap::do_insert (MetricSection* section)
                        if ((*i)->start().bars == section->start().bars) {
 
                                if (!(*i)->movable()) {
                        if ((*i)->start().bars == section->start().bars) {
 
                                if (!(*i)->movable()) {
-                                       
+
                                        /* can't (re)move this section, so overwrite
                                         * its data content (but not its properties as
                                         * a section
                                         */
                                        /* 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));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
                                        *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
-                                       
+
                                }
 
                                break;
                                }
 
                                break;
@@ -467,7 +483,7 @@ TempoMap::do_insert (MetricSection* section)
                                break;
                        }
                }
                                break;
                        }
                }
-               
+
                metrics.insert (i, section);
        }
 }
                metrics.insert (i, section);
        }
 }
@@ -475,17 +491,19 @@ TempoMap::do_insert (MetricSection* section)
 void
 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
 {
 void
 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
 {
-       const TempoSection& first (first_tempo());
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               TempoSection& first (first_tempo());
 
 
-       if (ts.start() != first.start()) {
-               remove_tempo (ts, false);
-               add_tempo (tempo, where);
-       } else {
-               {
-                       Glib::Threads::RWLock::WriterLock lm (lock);
-                       /* cannot move the first tempo section */
-                       *((Tempo*)&first) = tempo;
-                       recompute_map (false);
+               if (ts.start() != first.start()) {
+                       remove_tempo_locked (ts);
+                       add_tempo_locked (tempo, where, true);
+               } else {
+                       {
+                               /* cannot move the first tempo section */
+                               *static_cast<Tempo*>(&first) = tempo;
+                               recompute_map (false);
+                       }
                }
        }
 
                }
        }
 
@@ -497,64 +515,71 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
+               add_tempo_locked (tempo, where, true);
+       }
 
 
-               /* new tempos always start on a beat */
-               where.ticks = 0;
 
 
-               TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
-               
-               /* find the meter to use to set the bar offset of this
-                * tempo section.
-                */
+       PropertyChanged (PropertyChange ());
+}
 
 
-               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;
-                       }
-               }
+void
+TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute)
+{
+       /* new tempos always start on a beat */
+       where.ticks = 0;
 
 
-               ts->update_bar_offset_from_bbt (*meter);
+       TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
 
 
-               /* and insert it */
-               
-               do_insert (ts);
+       /* find the meter to use to set the bar offset of this
+        * tempo section.
+        */
 
 
-               recompute_map (false);
+       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);
 
 
-       PropertyChanged (PropertyChange ());
+       /* and insert it */
+
+       do_insert (ts);
+
+       if (recompute) {
+               recompute_map (false);
+       }
 }
 
 void
 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
 {
 }
 
 void
 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
 {
-       const MeterSection& first (first_meter());
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               MeterSection& first (first_meter());
 
 
-       if (ms.start() != first.start()) {
-               remove_meter (ms, false);
-               add_meter (meter, where);
-       } else {
-               {
-                       Glib::Threads::RWLock::WriterLock lm (lock);
+               if (ms.start() != first.start()) {
+                       remove_meter_locked (ms);
+                       add_meter_locked (meter, where, true);
+               } else {
                        /* cannot move the first meter section */
                        /* cannot move the first meter section */
-                       *((Meter*)&first) = meter;
+                       *static_cast<Meter*>(&first) = meter;
                        recompute_map (true);
                }
        }
                        recompute_map (true);
                }
        }
@@ -567,27 +592,10 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-
-               /* a new meter always starts a new bar on the first beat. so
-                  round the start time appropriately. remember that
-                  `where' is based on the existing tempo map, not
-                  the result after we insert the new meter.
-
-               */
-
-               if (where.beats != 1) {
-                       where.beats = 1;
-                       where.bars++;
-               }
-
-               /* new meters *always* start on a beat. */
-               where.ticks = 0;
-               
-               do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
-               recompute_map (true);
+               add_meter_locked (meter, where, true);
        }
 
        }
 
-       
+
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
                dump (std::cerr);
 #ifndef NDEBUG
        if (DEBUG_ENABLED(DEBUG::TempoMap)) {
                dump (std::cerr);
@@ -597,6 +605,32 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
        PropertyChanged (PropertyChange ());
 }
 
        PropertyChanged (PropertyChange ());
 }
 
+void
+TempoMap::add_meter_locked (const Meter& meter, 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
+          `where' is based on the existing tempo map, not
+          the result after we insert the new meter.
+
+       */
+
+       if (where.beats != 1) {
+               where.beats = 1;
+               where.bars++;
+       }
+
+       /* new meters *always* start on a beat. */
+       where.ticks = 0;
+
+       do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
+
+       if (recompute) {
+               recompute_map (true);
+       }
+
+}
+
 void
 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
 void
 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 {
@@ -605,7 +639,7 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                       { 
+                       {
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
                                recompute_map (false);
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
                                recompute_map (false);
@@ -677,7 +711,25 @@ TempoMap::first_meter () const
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
-       /*NOTREACHED*/
+       abort(); /*NOTREACHED*/
+       return *m;
+}
+
+MeterSection&
+TempoMap::first_meter ()
+{
+       MeterSection *m = 0;
+
+       /* CALLER MUST HOLD LOCK */
+
+       for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+                       return *m;
+               }
+       }
+
+       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
        return *m;
 }
 
        return *m;
 }
 
@@ -686,6 +738,8 @@ TempoMap::first_tempo () const
 {
        const TempoSection *t = 0;
 
 {
        const TempoSection *t = 0;
 
+       /* CALLER MUST HOLD LOCK */
+
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
                        return *t;
        for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
                if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
                        return *t;
@@ -693,7 +747,23 @@ TempoMap::first_tempo () const
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
        }
 
        fatal << _("programming error: no tempo section in tempo map!") << endmsg;
-       /*NOTREACHED*/
+       abort(); /*NOTREACHED*/
+       return *t;
+}
+
+TempoSection&
+TempoMap::first_tempo ()
+{
+       TempoSection *t = 0;
+
+       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
+                       return *t;
+               }
+       }
+
+       fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+       abort(); /*NOTREACHED*/
        return *t;
 }
 
        return *t;
 }
 
@@ -718,7 +788,7 @@ TempoMap::require_map_to (const BBT_Time& bbt)
         */
 
        int additional_minutes = 1;
         */
 
        int additional_minutes = 1;
-       
+
        while (1) {
                if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
                        break;
        while (1) {
                if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
                        break;
@@ -765,6 +835,8 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                }
        }
 
                }
        }
 
+       assert(meter);
+
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* ts;
 
        for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
                TempoSection* ts;
 
@@ -774,6 +846,8 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                }
        }
 
                }
        }
 
+       assert(tempo);
+
        /* assumes that the first meter & tempo are at frame zero */
        current_frame = 0;
        meter->set_frame (0);
        /* assumes that the first meter & tempo are at frame zero */
        current_frame = 0;
        meter->set_frame (0);
@@ -794,7 +868,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 
                        TempoSection* ts;
                        MeterSection* ms;
 
                        TempoSection* ts;
                        MeterSection* ms;
-       
+
                        if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
 
                                /* reassign the BBT time of this tempo section
                        if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
 
                                /* reassign the BBT time of this tempo section
@@ -807,7 +881,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                                rmeter = ms;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                rmeter = ms;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               /*NOTREACHED*/
+                               abort(); /*NOTREACHED*/
                        }
                }
        }
                        }
                }
        }
@@ -842,7 +916,7 @@ TempoMap::extend_map (framepos_t end)
                return;
        }
 
                return;
        }
 
-       BBTPointList::const_iterator i = _map.end();    
+       BBTPointList::const_iterator i = _map.end();
        Metrics::iterator next_metric;
 
        --i;
        Metrics::iterator next_metric;
 
        --i;
@@ -856,7 +930,7 @@ TempoMap::extend_map (framepos_t end)
        }
 
        /* find the metric immediately after the tempo + meter sections for the
        }
 
        /* find the metric immediately after the tempo + meter sections for the
-        * last point in the map 
+        * last point in the map
         */
 
        for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
         */
 
        for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
@@ -866,16 +940,16 @@ TempoMap::extend_map (framepos_t end)
        }
 
        /* we cast away const here because this is the one place where we need
        }
 
        /* we cast away const here because this is the one place where we need
-        * to actually modify the frame time of each metric section. 
+        * to actually modify the frame time of each metric section.
         */
 
         */
 
-       _extend_map (const_cast<TempoSection*> ((*i).tempo), 
+       _extend_map (const_cast<TempoSection*> ((*i).tempo),
                     const_cast<MeterSection*> ((*i).meter),
                     next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
 }
 
 void
                     const_cast<MeterSection*> ((*i).meter),
                     next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
 }
 
 void
-TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 
+TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
                       Metrics::iterator next_metric,
                       BBT_Time current, framepos_t current_frame, framepos_t end)
 {
                       Metrics::iterator next_metric,
                       BBT_Time current, framepos_t current_frame, framepos_t end)
 {
@@ -884,6 +958,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
        TempoSection* ts;
        MeterSection* ms;
        double beat_frames;
        TempoSection* ts;
        MeterSection* ms;
        double beat_frames;
+       double current_frame_exact;
        framepos_t bar_start_frame;
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
        framepos_t bar_start_frame;
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
@@ -895,11 +970,13 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
        }
 
        beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
        }
 
        beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
+       current_frame_exact = current_frame;
 
        while (current_frame < end) {
 
                current.beats++;
 
        while (current_frame < end) {
 
                current.beats++;
-               current_frame += beat_frames;
+               current_frame_exact += beat_frames;
+               current_frame = llrint(current_frame_exact);
 
                if (current.beats > meter->divisions_per_bar()) {
                        current.bars++;
 
                if (current.beats > meter->divisions_per_bar()) {
                        current.bars++;
@@ -930,27 +1007,28 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
                                         * is within, which will be different
                                         * from the preceding following ones
                                         * since it takes part of its duration
                                         * is within, which will be different
                                         * from the preceding following ones
                                         * since it takes part of its duration
-                                        * from the preceding tempo and part 
+                                        * from the preceding tempo and part
                                         * from this new tempo.
                                         */
 
                                        if (tempo->start().ticks != 0) {
                                         * from this new tempo.
                                         */
 
                                        if (tempo->start().ticks != 0) {
-                                               
-                                               double next_beat_frames = tempo->frames_per_beat (_frame_rate);                                 
-                                               
+
+                                               double next_beat_frames = tempo->frames_per_beat (_frame_rate);
+
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
                                                                                               tempo->start(), current_frame, tempo->bar_offset()));
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
                                                                                               tempo->start(), current_frame, tempo->bar_offset()));
-                                               
+
                                                /* back up to previous beat */
                                                /* back up to previous beat */
-                                               current_frame -= beat_frames;
+                                               current_frame_exact -= beat_frames;
+                                               current_frame = llrint(current_frame_exact);
 
                                                /* set tempo section location
                                                 * based on offset from last
 
                                                /* set tempo section location
                                                 * based on offset from last
-                                                * bar start 
+                                                * bar start
                                                 */
                                                 */
-                                               tempo->set_frame (bar_start_frame + 
+                                               tempo->set_frame (bar_start_frame +
                                                                  llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
                                                                  llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
-                                               
+
                                                /* advance to the location of
                                                 * the new (adjusted) beat. do
                                                 * this by figuring out the
                                                /* advance to the location of
                                                 * the new (adjusted) beat. do
                                                 * this by figuring out the
@@ -963,42 +1041,43 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
 
                                                double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
 
 
                                                double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
 
-                                               current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+                                               current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+                                               current_frame = llrint(current_frame_exact);
 
                                                /* next metric doesn't have to
                                                 * match this precisely to
                                                 * merit a reloop ...
                                                 */
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
 
                                                /* next metric doesn't have to
                                                 * match this precisely to
                                                 * merit a reloop ...
                                                 */
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
-                                               
+
                                        } else {
                                        } else {
-                                               
+
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
                                                                                               tempo->start(), current_frame));
                                                tempo->set_frame (current_frame);
                                        }
 
                                } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
                                                                                               tempo->start(), current_frame));
                                                tempo->set_frame (current_frame);
                                        }
 
                                } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
-                                       
+
                                        meter = ms;
 
                                        /* new meter section: always defines the
                                         * start of a bar.
                                         */
                                        meter = ms;
 
                                        /* new meter section: always defines the
                                         * start of a bar.
                                         */
-                                       
+
                                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
                                                                                       meter->start(), current, current_frame));
                                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
                                                                                       meter->start(), current, current_frame));
-                                       
+
                                        assert (current.beats == 1);
 
                                        meter->set_frame (current_frame);
                                }
                                        assert (current.beats == 1);
 
                                        meter->set_frame (current_frame);
                                }
-                               
+
                                beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
                                beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
-                               
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", 
+
+                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
                                                                               beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
                                                                               beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
-                       
+
                                ++next_metric;
 
                                if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
                                ++next_metric;
 
                                if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
@@ -1008,15 +1087,15 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
                                }
 
                        }
                                }
 
                        }
-               } 
+               }
 
                if (current.beats == 1) {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
 
                if (current.beats == 1) {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
-                       _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
+                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
                        bar_start_frame = current_frame;
                } else {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
                        bar_start_frame = current_frame;
                } else {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
-                       _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
+                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
                }
 
                if (next_metric == metrics.end()) {
                }
 
                if (next_metric == metrics.end()) {
@@ -1031,12 +1110,10 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
 }
 
 TempoMetric
 }
 
 TempoMetric
-TempoMap::metric_at (framepos_t frame) const
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
-       const Meter* meter;
-       const Tempo* tempo;
 
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
 
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
@@ -1051,16 +1128,13 @@ TempoMap::metric_at (framepos_t frame) const
                        break;
                }
 
                        break;
                }
 
-               if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       m.set_tempo (*tempo);
-               } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       m.set_meter (*meter);
-               }
+               m.set_metric(*i);
 
 
-               m.set_frame ((*i)->frame ());
-               m.set_start ((*i)->start ());
+               if (last) {
+                       *last = i;
+               }
        }
        }
-       
+
        return m;
 }
 
        return m;
 }
 
@@ -1069,8 +1143,6 @@ TempoMap::metric_at (BBT_Time bbt) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        TempoMetric m (first_meter(), first_tempo());
-       const Meter* meter;
-       const Tempo* tempo;
 
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
 
        /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
           at something, because we insert the default tempo and meter during
@@ -1087,14 +1159,7 @@ TempoMap::metric_at (BBT_Time bbt) const
                        break;
                }
 
                        break;
                }
 
-               if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
-                       m.set_tempo (*tempo);
-               } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
-                       m.set_meter (*meter);
-               }
-
-               m.set_frame ((*i)->frame ());
-               m.set_start (section_start);
+               m.set_metric (*i);
        }
 
        return m;
        }
 
        return m;
@@ -1126,7 +1191,7 @@ TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
        }
        if (!lm.locked()) {
                throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
        }
-       
+
        if (_map.empty() || _map.back().frame < frame) {
                throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
        }
        if (_map.empty() || _map.back().frame < frame) {
                throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
        }
@@ -1157,7 +1222,7 @@ TempoMap::frame_time (const BBT_Time& bbt)
                warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
                return 0;
        }
                warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
                return 0;
        }
-       
+
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
@@ -1170,7 +1235,7 @@ TempoMap::frame_time (const BBT_Time& bbt)
        BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
 
        if (bbt.ticks != 0) {
        BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
 
        if (bbt.ticks != 0) {
-               return ((*e).frame - (*s).frame) + 
+               return ((*e).frame - (*s).frame) +
                        llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
        } else {
                return ((*e).frame - (*s).frame);
                        llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
        } else {
                return ((*e).frame - (*s).frame);
@@ -1182,13 +1247,13 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
        BBT_Time when;
        bbt_time (pos, when);
 {
        BBT_Time when;
        bbt_time (pos, when);
-       
+
        Glib::Threads::RWLock::ReaderLock lm (lock);
        return bbt_duration_at_unlocked (when, bbt, dir);
 }
 
 framecnt_t
        Glib::Threads::RWLock::ReaderLock lm (lock);
        return bbt_duration_at_unlocked (when, bbt, dir);
 }
 
 framecnt_t
-TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/) 
+TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
 {
        if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
                return 0;
 {
        if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
                return 0;
@@ -1220,7 +1285,7 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i
        /* add any additional frames related to ticks in the added value */
 
        if (bbt.ticks != 0) {
        /* add any additional frames related to ticks in the added value */
 
        if (bbt.ticks != 0) {
-               return ((*wi).frame - (*start).frame) + 
+               return ((*wi).frame - (*start).frame) +
                        (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
        } else {
                return ((*wi).frame - (*start).frame);
                        (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
        } else {
                return ((*wi).frame - (*start).frame);
@@ -1228,19 +1293,19 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i
 }
 
 framepos_t
 }
 
 framepos_t
-TempoMap::round_to_bar (framepos_t fr, int dir)
+TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Bar);
 }
 
 framepos_t
 {
        return round_to_type (fr, dir, Bar);
 }
 
 framepos_t
-TempoMap::round_to_beat (framepos_t fr, int dir)
+TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
 {
        return round_to_type (fr, dir, Beat);
 }
 
 framepos_t
 {
        return round_to_type (fr, dir, Beat);
 }
 
 framepos_t
-TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
+TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
 {
        require_map_to (fr);
 
 {
        require_map_to (fr);
 
@@ -1248,7 +1313,6 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
        BBTPointList::const_iterator i = bbt_before_or_at (fr);
        BBT_Time the_beat;
        uint32_t ticks_one_subdivisions_worth;
        BBTPointList::const_iterator i = bbt_before_or_at (fr);
        BBT_Time the_beat;
        uint32_t ticks_one_subdivisions_worth;
-       uint32_t difference;
 
        bbt_time (fr, the_beat, i);
 
 
        bbt_time (fr, the_beat, i);
 
@@ -1259,11 +1323,14 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
 
        if (dir > 0) {
 
 
        if (dir > 0) {
 
-               /* round to next (even if we're on a subdivision */
+               /* round to next (or same iff dir == RoundUpMaybe) */
 
                uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
 
 
                uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
 
-               if (mod == 0) {
+               if (mod == 0 && dir == RoundUpMaybe) {
+                       /* right on the subdivision, which is fine, so do nothing */
+
+               } else if (mod == 0) {
                        /* right on the subdivision, so the difference is just the subdivision ticks */
                        the_beat.ticks += ticks_one_subdivisions_worth;
 
                        /* right on the subdivision, so the difference is just the subdivision ticks */
                        the_beat.ticks += ticks_one_subdivisions_worth;
 
@@ -1278,24 +1345,19 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
                        ++i;
                        assert (i != _map.end());
                        the_beat.ticks -= BBT_Time::ticks_per_beat;
                        ++i;
                        assert (i != _map.end());
                        the_beat.ticks -= BBT_Time::ticks_per_beat;
-               } 
+               }
 
 
        } else if (dir < 0) {
 
 
 
        } else if (dir < 0) {
 
-               /* round to previous (even if we're on a subdivision) */
+               /* round to previous (or same iff dir == RoundDownMaybe) */
 
 
-               uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
+               uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
 
 
-               if (mod == 0) {
-                       /* right on the subdivision, so the difference is just the subdivision ticks */
+               if (difference == 0 && dir == RoundDownAlways) {
+                       /* right on the subdivision, but force-rounding down,
+                          so the difference is just the subdivision ticks */
                        difference = ticks_one_subdivisions_worth;
                        difference = ticks_one_subdivisions_worth;
-               } else {
-                       /* not on subdivision, compute distance to previous subdivision, which
-                          is just the modulus.
-                       */
-
-                       difference = mod;
                }
 
                if (the_beat.ticks < difference) {
                }
 
                if (the_beat.ticks < difference) {
@@ -1315,9 +1377,9 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
                double rem;
 
                /* compute the distance to the previous and next subdivision */
                double rem;
 
                /* compute the distance to the previous and next subdivision */
-               
+
                if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
                if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
-                       
+
                        /* closer to the next subdivision, so shift forward */
 
                        the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
                        /* closer to the next subdivision, so shift forward */
 
                        the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
@@ -1330,10 +1392,10 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
                                assert (i != _map.end());
                                the_beat.ticks -= BBT_Time::ticks_per_beat;
                                DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
                                assert (i != _map.end());
                                the_beat.ticks -= BBT_Time::ticks_per_beat;
                                DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
-                       } 
+                       }
 
                } else if (rem > 0) {
 
                } else if (rem > 0) {
-                       
+
                        /* closer to previous subdivision, so shift backward */
 
                        if (rem > the_beat.ticks) {
                        /* closer to previous subdivision, so shift backward */
 
                        if (rem > the_beat.ticks) {
@@ -1354,12 +1416,12 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
                }
        }
 
                }
        }
 
-       return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * 
+       return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
                (*i).tempo->frames_per_beat (_frame_rate);
 }
 
 framepos_t
                (*i).tempo->frames_per_beat (_frame_rate);
 }
 
 framepos_t
-TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
+TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
        require_map_to (frame);
 
 {
        require_map_to (frame);
 
@@ -1376,7 +1438,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
 
        DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
                                                     (type == Bar ? "bar" : "beat")));
 
        DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
                                                     (type == Bar ? "bar" : "beat")));
-               
+
        switch (type) {
        case Bar:
                if (dir < 0) {
        switch (type) {
        case Bar:
                if (dir < 0) {
@@ -1387,6 +1449,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                        }
 
                        if ((*fi).is_bar() && (*fi).frame == frame) {
                        }
 
                        if ((*fi).is_bar() && (*fi).frame == frame) {
+                               if (dir == RoundDownMaybe) {
+                                       return frame;
+                               }
                                --fi;
                        }
 
                                --fi;
                        }
 
@@ -1396,7 +1461,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                                }
                                fi--;
                        }
                                }
                                fi--;
                        }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
 
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
 
@@ -1405,6 +1470,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                        /* find bar following 'frame' */
 
                        if ((*fi).is_bar() && (*fi).frame == frame) {
                        /* find bar following 'frame' */
 
                        if ((*fi).is_bar() && (*fi).frame == frame) {
+                               if (dir == RoundUpMaybe) {
+                                       return frame;
+                               }
                                ++fi;
                        }
 
                                ++fi;
                        }
 
@@ -1416,12 +1484,12 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                                }
                        }
 
                                }
                        }
 
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
 
                } else {
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
 
                } else {
-                       
+
                        /* true rounding: find nearest bar */
 
                        BBTPointList::const_iterator prev = fi;
                        /* true rounding: find nearest bar */
 
                        BBTPointList::const_iterator prev = fi;
@@ -1447,7 +1515,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                        } else {
                                return (*next).frame;
                        }
                        } else {
                                return (*next).frame;
                        }
-                       
+
                }
 
                break;
                }
 
                break;
@@ -1459,19 +1527,19 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                                return 0;
                        }
 
                                return 0;
                        }
 
-                       if ((*fi).frame >= frame) {
+                       if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) {
                                DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
                                --fi;
                        }
                                DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
                                --fi;
                        }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
                } else if (dir > 0) {
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
                } else if (dir > 0) {
-                       if ((*fi).frame <= frame) {
+                       if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) {
                                DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
                                ++fi;
                        }
                                DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
                                ++fi;
                        }
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
                } else {
                                                                     (*fi).bar, (*fi).beat, (*fi).frame));
                        return (*fi).frame;
                } else {
@@ -1488,7 +1556,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                           the beat before frame.
                        */
                        ++next;
                           the beat before frame.
                        */
                        ++next;
-                       
+
                        if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
                                return (*prev).frame;
                        } else {
                        if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
                                return (*prev).frame;
                        } else {
@@ -1498,17 +1566,16 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
                break;
        }
 
                break;
        }
 
-       /* NOTREACHED */
-       assert (false);
+       abort(); /* NOTREACHED */
        return 0;
 }
 
 void
        return 0;
 }
 
 void
-TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, 
-                   TempoMap::BBTPointList::const_iterator& end, 
-                   framepos_t lower, framepos_t upper) 
+TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
+                   TempoMap::BBTPointList::const_iterator& end,
+                   framepos_t lower, framepos_t upper)
 {
 {
-       { 
+       {
                Glib::Threads::RWLock::WriterLock lm (lock);
                if (_map.empty() || (_map.back().frame < upper)) {
                        recompute_map (false, upper);
                Glib::Threads::RWLock::WriterLock lm (lock);
                if (_map.empty() || (_map.back().frame < upper)) {
                        recompute_map (false, upper);
@@ -1541,6 +1608,7 @@ TempoMap::tempo_section_at (framepos_t frame) const
 
        if (prev == 0) {
                fatal << endmsg;
 
        if (prev == 0) {
                fatal << endmsg;
+               abort(); /*NOTREACHED*/
        }
 
        return *prev;
        }
 
        return *prev;
@@ -1553,6 +1621,33 @@ TempoMap::tempo_at (framepos_t frame) const
        return m.tempo();
 }
 
        return m.tempo();
 }
 
+const MeterSection&
+TempoMap::meter_section_at (framepos_t frame) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       Metrics::const_iterator i;
+       MeterSection* prev = 0;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* t;
+
+               if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
+
+                       if ((*i)->frame() > frame) {
+                               break;
+                       }
+
+                       prev = t;
+               }
+       }
+
+       if (prev == 0) {
+               fatal << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       return *prev;
+}
 
 const Meter&
 TempoMap::meter_at (framepos_t frame) const
 
 const Meter&
 TempoMap::meter_at (framepos_t frame) const
@@ -1590,7 +1685,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                metrics.clear();
 
                nlist = node.children();
                metrics.clear();
 
                nlist = node.children();
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
@@ -1603,7 +1698,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);
                                        if (ts->bar_offset() < 0.0) {
                                                if (last_meter) {
                                                        ts->update_bar_offset_from_bbt (*last_meter);
-                                               } 
+                                               }
                                        }
                                }
 
                                        }
                                }
 
@@ -1643,11 +1738,13 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                        if (prev != metrics.end()) {
                                if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
                                        if ((*prev)->start() == (*i)->start()) {
                        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;
                                                return -1;
                                        }
                                } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
                                        if ((*prev)->start() == (*i)->start()) {
                                                error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << 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;
                                                return -1;
                                        }
                                                error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
                                                return -1;
                                        }
@@ -1734,23 +1831,23 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                const TempoSection* tempo;
                MeterSection *m;
                TempoSection *t;
                const TempoSection* tempo;
                MeterSection *m;
                TempoSection *t;
-               
+
                meter = &first_meter ();
                tempo = &first_tempo ();
                meter = &first_meter ();
                tempo = &first_tempo ();
-               
+
                BBT_Time start;
                BBT_Time end;
                BBT_Time start;
                BBT_Time end;
-               
+
                // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
                // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
-               
+
                bool first = true;
                MetricSection* prev = 0;
                bool first = true;
                MetricSection* prev = 0;
-               
+
                for (i = metrics.begin(); i != metrics.end(); ++i) {
                for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       
+
                        BBT_Time bbt;
                        TempoMetric metric (*meter, *tempo);
                        BBT_Time bbt;
                        TempoMetric metric (*meter, *tempo);
-                       
+
                        if (prev) {
                                metric.set_start (prev->start());
                                metric.set_frame (prev->frame());
                        if (prev) {
                                metric.set_start (prev->start());
                                metric.set_frame (prev->frame());
@@ -1758,34 +1855,34 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                // metric will be at frames=0 bbt=1|1|0 by default
                                // which is correct for our purpose
                        }
                                // metric will be at frames=0 bbt=1|1|0 by default
                                // which is correct for our purpose
                        }
-                       
+
                        BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
                        bbt_time ((*i)->frame(), bbt, bi);
                        BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
                        bbt_time ((*i)->frame(), bbt, bi);
-                       
+
                        // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
                        // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
-                       
+
                        if (first) {
                                first = false;
                        } else {
                        if (first) {
                                first = false;
                        } else {
-                               
+
                                if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
                                        /* round up to next beat */
                                        bbt.beats += 1;
                                }
                                if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
                                        /* round up to next beat */
                                        bbt.beats += 1;
                                }
-                               
+
                                bbt.ticks = 0;
                                bbt.ticks = 0;
-                               
+
                                if (bbt.beats != 1) {
                                        /* round up to next bar */
                                        bbt.bars += 1;
                                        bbt.beats = 1;
                                }
                        }
                                if (bbt.beats != 1) {
                                        /* round up to next bar */
                                        bbt.bars += 1;
                                        bbt.beats = 1;
                                }
                        }
-                       
+
                        // cerr << bbt << endl;
                        // cerr << bbt << endl;
-                       
+
                        (*i)->set_start (bbt);
                        (*i)->set_start (bbt);
-                       
+
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
                                tempo = t;
                                // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
                                tempo = t;
                                // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
@@ -1794,24 +1891,84 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
                        } else {
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
-                               /*NOTREACHED*/
+                               abort(); /*NOTREACHED*/
                        }
                        }
-                       
+
                        prev = (*i);
                }
                        prev = (*i);
                }
-               
+
                recompute_map (true);
        }
 
 
        PropertyChanged (PropertyChange ());
 }
                recompute_map (true);
        }
 
 
        PropertyChanged (PropertyChange ());
 }
+bool
+TempoMap::remove_time (framepos_t where, framecnt_t amount)
+{
+       bool moved = false;
+
+       std::list<MetricSection*> metric_kill_list;
+
+       TempoSection* last_tempo = NULL;
+       MeterSection* last_meter = NULL;
+       bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
+       bool meter_after = false; // is there a meter marker likewise?
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+                       if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
+                               metric_kill_list.push_back(*i);
+                               TempoSection *lt = dynamic_cast<TempoSection*> (*i);
+                               if (lt)
+                                       last_tempo = lt;
+                               MeterSection *lm = dynamic_cast<MeterSection*> (*i);
+                               if (lm)
+                                       last_meter = lm;
+                       }
+                       else if ((*i)->frame() >= where) {
+                               // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
+                               (*i)->set_frame ((*i)->frame() - amount);
+                               if ((*i)->frame() == where) {
+                                       // marker was immediately after end of range
+                                       tempo_after = dynamic_cast<TempoSection*> (*i);
+                                       meter_after = dynamic_cast<MeterSection*> (*i);
+                               }
+                               moved = true;
+                       }
+               }
+
+               //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
+               if (last_tempo && !tempo_after) {
+                       metric_kill_list.remove(last_tempo);
+                       last_tempo->set_frame(where);
+                       moved = true;
+               }
+               if (last_meter && !meter_after) {
+                       metric_kill_list.remove(last_meter);
+                       last_meter->set_frame(where);
+                       moved = true;
+               }
+
+               //remove all the remaining metrics
+               for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
+                       metrics.remove(*i);
+                       moved = true;
+               }
+
+               if (moved) {
+                       recompute_map (true);
+               }
+       }
+       PropertyChanged (PropertyChange ());
+       return moved;
+}
 
 /** Add some (fractional) beats to a session frame position, and return the result in frames.
  *  pos can be -ve, if required.
  */
 framepos_t
 
 /** Add some (fractional) beats to a session frame position, and return the result in frames.
  *  pos can be -ve, if required.
  */
 framepos_t
-TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        Metrics::const_iterator next_tempo;
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        Metrics::const_iterator next_tempo;
@@ -1835,11 +1992,11 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                        if (pos < 0 && f == 0) {
                                f = pos;
                        }
                        if (pos < 0 && f == 0) {
                                f = pos;
                        }
-                       
+
                        if (f > pos) {
                                break;
                        }
                        if (f > pos) {
                                break;
                        }
-                       
+
                        tempo = t;
                }
        }
                        tempo = t;
                }
        }
@@ -1849,21 +2006,23 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
           tempo       -> the Tempo for "pos"
           next_tempo  -> first tempo after "pos", possibly metrics.end()
        */
           tempo       -> the Tempo for "pos"
           next_tempo  -> first tempo after "pos", possibly metrics.end()
        */
+       assert(tempo);
 
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
                                     pos, beats, *((const Tempo*)tempo), tempo->frame()));
 
 
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
                                     pos, beats, *((const Tempo*)tempo), tempo->frame()));
 
-       while (beats) {
+       while (!!beats) {
 
                /* Distance to the end of this section in frames */
                framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
 
                /* Distance to the end in beats */
 
                /* Distance to the end of this section in frames */
                framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
 
                /* Distance to the end in beats */
-               Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
+               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
+                       distance_frames, tempo->frames_per_beat (_frame_rate));
 
                /* Amount to subtract this time */
 
                /* Amount to subtract this time */
-               double const delta = min (distance_beats, beats);
+               Evoral::Beats const delta = min (distance_beats, beats);
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
                                                               (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
                                                               (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
@@ -1871,7 +2030,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
 
                /* Update */
                beats -= delta;
 
                /* Update */
                beats -= delta;
-               pos += delta * tempo->frames_per_beat (_frame_rate);
+               pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate));
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
 
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
 
@@ -1888,7 +2047,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
-                               
+
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
@@ -1901,7 +2060,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
 
 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
 framepos_t
 
 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
 framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
+TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        Metrics::const_reverse_iterator prev_tempo;
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
        Metrics::const_reverse_iterator prev_tempo;
@@ -1933,7 +2092,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                           keep going to get the previous tempo (or
                           metrics.rend())
                        */
                           keep going to get the previous tempo (or
                           metrics.rend())
                        */
-                       
+
                        if (f <= pos) {
                                if (tempo == 0) {
                                        /* first tempo with position at or
                        if (f <= pos) {
                                if (tempo == 0) {
                                        /* first tempo with position at or
@@ -1950,6 +2109,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                }
        }
 
                }
        }
 
+       assert(tempo);
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
                                     pos, beats, *((const Tempo*)tempo), tempo->frame(),
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
                                     pos, beats, *((const Tempo*)tempo), tempo->frame(),
@@ -1961,23 +2121,24 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
           prev_tempo  -> the first metric before "pos", possibly metrics.rend()
        */
 
           prev_tempo  -> the first metric before "pos", possibly metrics.rend()
        */
 
-       while (beats) {
-               
+       while (!!beats) {
+
                /* Distance to the start of this section in frames */
                framecnt_t distance_frames = (pos - tempo->frame());
 
                /* Distance to the start in beats */
                /* Distance to the start of this section in frames */
                framecnt_t distance_frames = (pos - tempo->frame());
 
                /* Distance to the start in beats */
-               Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
+               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
+                       distance_frames, tempo->frames_per_beat (_frame_rate));
 
                /* Amount to subtract this time */
 
                /* Amount to subtract this time */
-               double const sub = min (distance_beats, beats);
+               Evoral::Beats const sub = min (distance_beats, beats);
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
                                                               tempo->frame(), distance_frames, distance_beats));
                /* Update */
 
                beats -= sub;
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
                                                               tempo->frame(), distance_frames, distance_beats));
                /* Update */
 
                beats -= sub;
-               pos -= sub * tempo->frames_per_beat (_frame_rate);
+               pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate);
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
                                                               (prev_tempo == metrics.rend())));
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
                                                               (prev_tempo == metrics.rend())));
@@ -2002,8 +2163,8 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                                }
                        }
                } else {
                                }
                        }
                } else {
-                       pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
-                       beats = 0;
+                       pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
+                       beats = Evoral::Beats();
                }
        }
 
                }
        }
 
@@ -2076,7 +2237,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                 * traversed before we change the
                                 * frames_per_beat value.
                                 */
                                 * traversed before we change the
                                 * frames_per_beat value.
                                 */
-                               
+
                                pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
                                bars = 0;
 
                                pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
                                bars = 0;
 
@@ -2136,7 +2297,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        if (op.ticks) {
                if (op.ticks >= BBT_Time::ticks_per_beat) {
                        pos += llrint (frames_per_beat + /* extra beat */
        if (op.ticks) {
                if (op.ticks >= BBT_Time::ticks_per_beat) {
                        pos += llrint (frames_per_beat + /* extra beat */
-                                      (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / 
+                                      (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
                                                           (double) BBT_Time::ticks_per_beat)));
                } else {
                        pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
                                                           (double) BBT_Time::ticks_per_beat)));
                } else {
                        pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
@@ -2149,7 +2310,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 /** Count the number of beats that are equivalent to distance when going forward,
     starting at pos.
 */
 /** Count the number of beats that are equivalent to distance when going forward,
     starting at pos.
 */
-Evoral::MusicalTime
+Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
@@ -2178,14 +2339,13 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
           tempo -> the Tempo for "pos"
           next_tempo -> the next tempo after "pos", possibly metrics.end()
        */
           tempo -> the Tempo for "pos"
           next_tempo -> the next tempo after "pos", possibly metrics.end()
        */
-
        assert (tempo);
 
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
                                     pos, distance, *((const Tempo*)tempo), tempo->frame()));
        assert (tempo);
 
        DEBUG_TRACE (DEBUG::TempoMath,
                     string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
                                     pos, distance, *((const Tempo*)tempo), tempo->frame()));
-       
-       Evoral::MusicalTime beats = 0;
+
+       Evoral::Beats beats = Evoral::Beats();
 
        while (distance) {
 
 
        while (distance) {
 
@@ -2203,8 +2363,8 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
                        distance_to_end = end - pos;
                }
 
                        distance_to_end = end - pos;
                }
 
-               /* Amount to subtract this time */
-               double const sub = min (distance, distance_to_end);
+               /* Amount to subtract this time in frames */
+               framecnt_t const sub = min (distance, distance_to_end);
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
                                                               distance_to_end, sub));
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
                                                               distance_to_end, sub));
@@ -2213,7 +2373,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
                pos += sub;
                distance -= sub;
                assert (tempo);
                pos += sub;
                distance -= sub;
                assert (tempo);
-               beats += sub / tempo->frames_per_beat (_frame_rate);
+               beats += Evoral::Beats::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate));
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
                                                               pos, beats, distance));
 
                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
                                                               pos, beats, distance));
@@ -2232,7 +2392,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
-                               
+
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
@@ -2261,7 +2421,7 @@ TempoMap::bbt_before_or_at (framepos_t pos)
 
        if (pos < 0) {
                /* not really correct, but we should catch pos < 0 at a higher
 
        if (pos < 0) {
                /* not really correct, but we should catch pos < 0 at a higher
-                  level 
+                  level
                */
                return _map.begin();
        }
                */
                return _map.begin();
        }
@@ -2297,7 +2457,7 @@ TempoMap::bbt_before_or_at (const BBT_Time& bbt)
 }
 
 TempoMap::BBTPointList::const_iterator
 }
 
 TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_after_or_at (framepos_t pos) 
+TempoMap::bbt_after_or_at (framepos_t pos)
 {
        /* CALLER MUST HOLD READ LOCK */
 
 {
        /* CALLER MUST HOLD READ LOCK */
 
@@ -2315,17 +2475,17 @@ TempoMap::bbt_after_or_at (framepos_t pos)
        return i;
 }
 
        return i;
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
 }
 
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const Tempo& t) {
        return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
 }
 
 operator<< (std::ostream& o, const Tempo& t) {
        return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const MetricSection& section) {
 
        o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
 operator<< (std::ostream& o, const MetricSection& section) {
 
        o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';