plugin-pin management.. nearly there
[ardour.git] / libs / ardour / tempo.cc
index 1767c8100dce0e738f16cceeb16e54958ef079ad..e2de5ba49f7c5c695126b54c4f377733f334ce0a 100644 (file)
@@ -25,8 +25,9 @@
 
 #include <glibmm/threads.h>
 #include "pbd/xml++.h"
-#include "evoral/types.hpp"
+#include "evoral/Beats.hpp"
 #include "ardour/debug.h"
+#include "ardour/lmath.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
-          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".
@@ -73,7 +74,7 @@ TempoSection::TempoSection (const XMLNode& node)
 {
        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;
@@ -132,7 +133,7 @@ TempoSection::get_state() const
 {
        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,
@@ -155,7 +156,7 @@ void
 
 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()));
@@ -172,15 +173,15 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
        }
 
        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;
-                                           
-       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);
@@ -195,7 +196,7 @@ MeterSection::MeterSection (const XMLNode& node)
 {
        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;
@@ -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 (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];
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
                  start().bars,
@@ -306,23 +307,11 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
 
        {
                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) {
@@ -330,6 +319,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)
 {
@@ -337,23 +345,11 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
 
        {
                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) {
@@ -361,6 +357,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)
 {
@@ -369,7 +384,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
-        * measure. 
+        * measure.
         */
 
        if (dynamic_cast<MeterSection*>(section)) {
@@ -377,21 +392,21 @@ TempoMap::do_insert (MetricSection* section)
                /* we need to (potentially) update the BBT times of tempo
                   sections based on this new meter.
                */
-               
+
                if ((section->start().beats != 1) || (section->start().ticks != 0)) {
-                       
+
                        BBT_Time corrected = section->start();
                        corrected.beats = 1;
                        corrected.ticks = 0;
-                       
+
                        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);
                }
        }
 
-       
+
 
        /* 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 +428,19 @@ TempoMap::do_insert (MetricSection* section)
                            (*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).
                                         */
-                                       
+
                                        *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
                                }
                                break;
-                       } 
+                       }
 
                } else if (!iter_is_tempo && !insert_is_tempo) {
 
@@ -434,17 +449,17 @@ TempoMap::do_insert (MetricSection* section)
                        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
                                         */
-                                       
+
                                        *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
                                        need_add = false;
                                } else {
                                        metrics.erase (i);
-                                       
+
                                }
 
                                break;
@@ -467,7 +482,7 @@ TempoMap::do_insert (MetricSection* section)
                                break;
                        }
                }
-               
+
                metrics.insert (i, section);
        }
 }
@@ -475,17 +490,19 @@ TempoMap::do_insert (MetricSection* section)
 void
 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
 {
-       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 */
-                       *static_cast<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,62 +514,69 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
 {
        {
                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)
 {
-       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 */
                        *static_cast<Meter*>(&first) = meter;
                        recompute_map (true);
@@ -567,27 +591,10 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
 {
        {
                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);
@@ -597,6 +604,32 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
        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)
 {
@@ -605,7 +638,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) {
-                       { 
+                       {
                                Glib::Threads::RWLock::WriterLock lm (lock);
                                *((Tempo*) t) = newtempo;
                                recompute_map (false);
@@ -686,6 +719,8 @@ 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;
@@ -702,6 +737,8 @@ TempoMap::first_tempo () const
 {
        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;
@@ -750,7 +787,7 @@ TempoMap::require_map_to (const BBT_Time& bbt)
         */
 
        int additional_minutes = 1;
-       
+
        while (1) {
                if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
                        break;
@@ -830,7 +867,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 
                        TempoSection* ts;
                        MeterSection* ms;
-       
+
                        if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
 
                                /* reassign the BBT time of this tempo section
@@ -878,7 +915,7 @@ TempoMap::extend_map (framepos_t end)
                return;
        }
 
-       BBTPointList::const_iterator i = _map.end();    
+       BBTPointList::const_iterator i = _map.end();
        Metrics::iterator next_metric;
 
        --i;
@@ -892,7 +929,7 @@ TempoMap::extend_map (framepos_t end)
        }
 
        /* 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) {
@@ -902,16 +939,16 @@ TempoMap::extend_map (framepos_t end)
        }
 
        /* 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
-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)
 {
@@ -969,28 +1006,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
-                                        * from the preceding tempo and part 
+                                        * from the preceding tempo and part
                                         * 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()));
-                                               
+
                                                /* back up to previous beat */
                                                current_frame_exact -= beat_frames;
                                                current_frame = llrint(current_frame_exact);
 
                                                /* 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)));
-                                               
+
                                                /* advance to the location of
                                                 * the new (adjusted) beat. do
                                                 * this by figuring out the
@@ -1011,35 +1048,35 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
                                                 * merit a reloop ...
                                                 */
                                                DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
-                                               
+
                                        } 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) {
-                                       
+
                                        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));
-                                       
+
                                        assert (current.beats == 1);
 
                                        meter->set_frame (current_frame);
                                }
-                               
+
                                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)));
-                       
+
                                ++next_metric;
 
                                if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
@@ -1049,7 +1086,7 @@ 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));
@@ -1096,7 +1133,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
                        *last = i;
                }
        }
-       
+
        return m;
 }
 
@@ -1153,7 +1190,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 (_map.empty() || _map.back().frame < frame) {
                throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
        }
@@ -1184,7 +1221,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;
        }
-       
+
        if (bbt.beats < 1) {
                throw std::logic_error ("beats are counted from one");
        }
@@ -1197,7 +1234,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) {
-               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);
@@ -1209,13 +1246,13 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
 {
        BBT_Time when;
        bbt_time (pos, when);
-       
+
        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;
@@ -1247,7 +1284,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) {
-               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);
@@ -1307,7 +1344,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        ++i;
                        assert (i != _map.end());
                        the_beat.ticks -= BBT_Time::ticks_per_beat;
-               } 
+               }
 
 
        } else if (dir < 0) {
@@ -1339,9 +1376,9 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                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) {
-                       
+
                        /* closer to the next subdivision, so shift forward */
 
                        the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
@@ -1354,10 +1391,10 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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));
-                       } 
+                       }
 
                } else if (rem > 0) {
-                       
+
                        /* closer to previous subdivision, so shift backward */
 
                        if (rem > the_beat.ticks) {
@@ -1378,7 +1415,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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);
 }
 
@@ -1400,7 +1437,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode 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")));
-               
+
        switch (type) {
        case Bar:
                if (dir < 0) {
@@ -1423,7 +1460,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                                }
                                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;
 
@@ -1446,12 +1483,12 @@ TempoMap::round_to_type (framepos_t frame, RoundMode 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 {
-                       
+
                        /* true rounding: find nearest bar */
 
                        BBTPointList::const_iterator prev = fi;
@@ -1477,7 +1514,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                        } else {
                                return (*next).frame;
                        }
-                       
+
                }
 
                break;
@@ -1493,7 +1530,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                                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) {
@@ -1501,7 +1538,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                                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 {
@@ -1518,7 +1555,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
                           the beat before frame.
                        */
                        ++next;
-                       
+
                        if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
                                return (*prev).frame;
                        } else {
@@ -1533,11 +1570,11 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 }
 
 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);
@@ -1583,6 +1620,33 @@ TempoMap::tempo_at (framepos_t frame) const
        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
@@ -1620,7 +1684,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/)
                metrics.clear();
 
                nlist = node.children();
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        XMLNode* child = *niter;
 
@@ -1633,7 +1697,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);
-                                               } 
+                                               }
                                        }
                                }
 
@@ -1673,11 +1737,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()) {
+                                               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()) {
+                                               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;
                                        }
@@ -1764,23 +1830,23 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                const TempoSection* tempo;
                MeterSection *m;
                TempoSection *t;
-               
+
                meter = &first_meter ();
                tempo = &first_tempo ();
-               
+
                BBT_Time start;
                BBT_Time end;
-               
+
                // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
-               
+
                bool first = true;
                MetricSection* prev = 0;
-               
+
                for (i = metrics.begin(); i != metrics.end(); ++i) {
-                       
+
                        BBT_Time bbt;
                        TempoMetric metric (*meter, *tempo);
-                       
+
                        if (prev) {
                                metric.set_start (prev->start());
                                metric.set_frame (prev->frame());
@@ -1788,34 +1854,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
                        }
-                       
+
                        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 << " => ";
-                       
+
                        if (first) {
                                first = false;
                        } else {
-                               
+
                                if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
                                        /* round up to next beat */
                                        bbt.beats += 1;
                                }
-                               
+
                                bbt.ticks = 0;
-                               
+
                                if (bbt.beats != 1) {
                                        /* round up to next bar */
                                        bbt.bars += 1;
                                        bbt.beats = 1;
                                }
                        }
-                       
+
                        // cerr << bbt << endl;
-                       
+
                        (*i)->set_start (bbt);
-                       
+
                        if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
                                tempo = t;
                                // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
@@ -1826,22 +1892,82 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                fatal << _("programming error: unhandled MetricSection type") << endmsg;
                                abort(); /*NOTREACHED*/
                        }
-                       
+
                        prev = (*i);
                }
-               
+
                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
-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;
@@ -1865,11 +1991,11 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                        if (pos < 0 && f == 0) {
                                f = pos;
                        }
-                       
+
                        if (f > pos) {
                                break;
                        }
-                       
+
                        tempo = t;
                }
        }
@@ -1885,17 +2011,17 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                     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 */
-               Evoral::MusicalTime distance_beats = Evoral::MusicalTime::ticks_at_rate(
+               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
                        distance_frames, tempo->frames_per_beat (_frame_rate));
 
                /* Amount to subtract this time */
-               Evoral::MusicalTime 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()),
@@ -1920,7 +2046,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
-                               
+
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
@@ -1933,7 +2059,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
-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;
@@ -1965,7 +2091,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                           keep going to get the previous tempo (or
                           metrics.rend())
                        */
-                       
+
                        if (f <= pos) {
                                if (tempo == 0) {
                                        /* first tempo with position at or
@@ -1994,24 +2120,24 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
           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 */
-               Evoral::MusicalTime distance_beats = Evoral::MusicalTime::ticks_at_rate(
+               Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
                        distance_frames, tempo->frames_per_beat (_frame_rate));
 
                /* Amount to subtract this time */
-               Evoral::MusicalTime 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;
-               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())));
@@ -2036,8 +2162,8 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
                                }
                        }
                } else {
-                       pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
-                       beats = Evoral::MusicalTime();
+                       pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
+                       beats = Evoral::Beats();
                }
        }
 
@@ -2110,7 +2236,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                 * traversed before we change the
                                 * frames_per_beat value.
                                 */
-                               
+
                                pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
                                bars = 0;
 
@@ -2170,7 +2296,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 */
-                                      (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));
@@ -2183,7 +2309,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.
 */
-Evoral::MusicalTime
+Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
@@ -2217,8 +2343,8 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
        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 = Evoral::MusicalTime();
+
+       Evoral::Beats beats = Evoral::Beats();
 
        while (distance) {
 
@@ -2246,7 +2372,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
                pos += sub;
                distance -= sub;
                assert (tempo);
-               beats += Evoral::MusicalTime::ticks_at_rate(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));
@@ -2265,7 +2391,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
                        while (next_tempo != metrics.end ()) {
 
                                ++next_tempo;
-                               
+
                                if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
                                        break;
                                }
@@ -2294,7 +2420,7 @@ TempoMap::bbt_before_or_at (framepos_t pos)
 
        if (pos < 0) {
                /* not really correct, but we should catch pos < 0 at a higher
-                  level 
+                  level
                */
                return _map.begin();
        }
@@ -2330,7 +2456,7 @@ TempoMap::bbt_before_or_at (const BBT_Time& bbt)
 }
 
 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 */
 
@@ -2348,17 +2474,17 @@ TempoMap::bbt_after_or_at (framepos_t pos)
        return i;
 }
 
-std::ostream& 
+std::ostream&
 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";
 }
 
-std::ostream& 
+std::ostream&
 operator<< (std::ostream& o, const MetricSection& section) {
 
        o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';