Initial stab at tempo ramps.
authornick_m <mainsbridge@gmail.com>
Sat, 19 Dec 2015 17:41:45 +0000 (04:41 +1100)
committernick_m <mainsbridge@gmail.com>
Fri, 27 May 2016 13:38:09 +0000 (23:38 +1000)
Replaces the list of points in TempoMap with TempoSection functions, which
compute tempo-at or tick-at time relative to tempo section start.
TempoMap consults them additively to determine things like bbt_time(),
frame_time() get_grid() etc.
This has a marked effect on scrolling speed along with the code simplification
in the places it has been attempted.

Several things are broken here.
Currently every ramp except the last one is an exponential ramp. this may
be simple to fix :).
Mouse-over midi grid doesn't match mouse click grid. should also be simple.

Many things seem to work, but their accuracy should be in question until
each area has been addressed.

16 files changed:
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_rulers.cc
gtk2_ardour/editor_tempodisplay.cc
gtk2_ardour/tempo_dialog.cc
gtk2_ardour/tempo_dialog.h
gtk2_ardour/tempo_lines.cc
gtk2_ardour/tempo_lines.h
libs/ardour/ardour/tempo.h
libs/ardour/session_click.cc
libs/ardour/session_vst.cc
libs/ardour/tempo.cc
libs/backends/jack/jack_session.cc
libs/canvas/ruler.cc

index 5129a1ffaa794baa2a03fe3a610c3967b0b2ddb0..7353b6ff38a7bc9db6f09db1efcfb5a836fb0d5e 100644 (file)
@@ -2185,14 +2185,10 @@ Editor::set_snap_to (SnapType st)
        case SnapToBeatDiv4:
        case SnapToBeatDiv3:
        case SnapToBeatDiv2: {
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin;
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end;
-
-               compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(),
-                                           current_bbt_points_begin, current_bbt_points_end);
-               compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples(),
-                                        current_bbt_points_begin, current_bbt_points_end);
-               update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end);
+               std::vector<TempoMap::BBTPoint> grid;
+               compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
+               compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame + current_page_samples());
+               update_tempo_based_rulers (grid);
                break;
        }
 
@@ -3952,11 +3948,9 @@ Editor::set_show_measures (bool yn)
                                tempo_lines->show();
                        }
 
-                       ARDOUR::TempoMap::BBTPointList::const_iterator begin;
-                       ARDOUR::TempoMap::BBTPointList::const_iterator end;
-
-                       compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end);
-                       draw_measures (begin, end);
+                       std::vector<TempoMap::BBTPoint> grid;
+                       compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
+                       draw_measures (grid);
                }
 
                instant_save ();
@@ -4582,14 +4576,10 @@ Editor::visual_changer (const VisualChange& vc)
 
                compute_fixed_ruler_scale ();
 
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin;
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end;
-
-               compute_current_bbt_points (vc.time_origin, pending_visual_change.time_origin + current_page_samples(),
-                                           current_bbt_points_begin, current_bbt_points_end);
-               compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples(),
-                                        current_bbt_points_begin, current_bbt_points_end);
-               update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end);
+               std::vector<TempoMap::BBTPoint> grid;
+               compute_current_bbt_points (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples());
+               compute_bbt_ruler_scale (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples());
+               update_tempo_based_rulers (grid);
 
                update_video_timeline();
        }
index 665e9cc54cc0f961132b223e03494abe0c5ae8b8..139e044eae9d23236100a86c4e8cb3d93754f66e 100644 (file)
@@ -885,8 +885,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void update_just_timecode ();
        void compute_fixed_ruler_scale (); //calculates the RulerScale of the fixed rulers
        void update_fixed_rulers ();
-       void update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                                       ARDOUR::TempoMap::BBTPointList::const_iterator& end);
+       void update_tempo_based_rulers (std::vector<ARDOUR::TempoMap::BBTPoint>& grid);
        void popup_ruler_menu (framepos_t where = 0, ItemType type = RegionItem);
        void update_ruler_visibility ();
        void set_ruler_visible (RulerType, bool);
@@ -949,9 +948,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        gint bbt_nmarks;
        uint32_t bbt_bar_helper_on;
        uint32_t bbt_accent_modulo;
-       void compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
-                                     ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin,
-                                     ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end);
+       void compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper);
 
        ArdourCanvas::Ruler* timecode_ruler;
        ArdourCanvas::Ruler* bbt_ruler;
@@ -1636,8 +1633,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        ArdourCanvas::Container* time_line_group;
 
        void hide_measures ();
-       void draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                           ARDOUR::TempoMap::BBTPointList::const_iterator& end);
+       void draw_measures (std::vector<ARDOUR::TempoMap::BBTPoint>&);
 
        void new_tempo_section ();
 
@@ -1698,9 +1694,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void remove_metric_marks ();
        void draw_metric_marks (const ARDOUR::Metrics& metrics);
 
-       void compute_current_bbt_points (framepos_t left, framepos_t right,
-                                        ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                                        ARDOUR::TempoMap::BBTPointList::const_iterator& end);
+       void compute_current_bbt_points (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t left, framepos_t right);
 
        void tempo_map_changed (const PBD::PropertyChange&);
        void redisplay_tempo (bool immediate_redraw);
index d04b52c306598b3105532a06f65802210dba0dd6..f6f7f2b67c9baef1a88db2f95ea18c35b169e040 100644 (file)
@@ -3344,14 +3344,14 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        if (_copy == true) {
                _editor->begin_reversible_command (_("copy tempo mark"));
                XMLNode &before = map.get_state();
-               map.add_tempo (_marker->tempo(), when);
+               map.add_tempo (_marker->tempo(), when, _marker->tempo().type());
                XMLNode &after = map.get_state();
                _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
                _editor->commit_reversible_command ();
 
        } else {
                /* we removed it before, so add it back now */
-               map.add_tempo (_marker->tempo(), when);
+               map.add_tempo (_marker->tempo(), when, _marker->tempo().type());
                XMLNode &after = map.get_state();
                _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
                _editor->commit_reversible_command ();
@@ -3369,7 +3369,7 @@ TempoMarkerDrag::aborted (bool moved)
        if (moved) {
                TempoMap& map (_editor->session()->tempo_map());
                /* we removed it before, so add it back now */
-               map.add_tempo (_marker->tempo(), _marker->tempo().start());
+               map.add_tempo (_marker->tempo(), _marker->tempo().start(), _marker->tempo().type());
                // delete the dummy marker we used for visual representation while moving.
                // a new visual marker will show up automatically.
                delete _marker;
index 66ef92baa79121bc1a7e798fa3d80bc21404dd9e..8302f1b77811d6ecf18b1ad014e29146a080a24e 100644 (file)
@@ -6552,7 +6552,7 @@ Editor::define_one_bar (framepos_t start, framepos_t end)
        } else {
                Timecode::BBT_Time bbt;
                _session->tempo_map().bbt_time (start, bbt);
-               _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
+               _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt, TempoSection::TempoSectionType::Ramp);
        }
 
        XMLNode& after (_session->tempo_map().get_state());
index bb45e97bb336a2349a3f213b1c1c5f2abd5f1e63..ca450594e6c73cf99e8621897c70cdfbb6caa1ed 100644 (file)
@@ -710,15 +710,13 @@ Editor::update_fixed_rulers ()
 }
 
 void
-Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                                   ARDOUR::TempoMap::BBTPointList::const_iterator& end)
+Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid)
 {
        if (_session == 0) {
                return;
        }
 
-       compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
-                                begin, end);
+       compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples());
 
        _bbt_metric->units_per_pixel = samples_per_pixel;
 
@@ -1011,19 +1009,19 @@ Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdou
 }
 
 void
-Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
-                                ARDOUR::TempoMap::BBTPointList::const_iterator begin,
-                                ARDOUR::TempoMap::BBTPointList::const_iterator end)
+Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper)
 {
        if (_session == 0) {
                return;
        }
 
-       TempoMap::BBTPointList::const_iterator i;
+       std::vector<TempoMap::BBTPoint>::const_iterator i;
        Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
+       framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (lower)));
+       framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (upper)) + 1.0);
 
-       _session->bbt_time (lower, lower_beat);
-       _session->bbt_time (upper, upper_beat);
+       _session->bbt_time (beat_before_lower_pos, lower_beat);
+       _session->bbt_time (beat_after_upper_pos, upper_beat);
        uint32_t beats = 0;
 
        bbt_accent_modulo = 1;
@@ -1103,19 +1101,21 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
                 bbt_beat_subdivision = 4;
                break;
        }
-
-       if (distance (begin, end) == 0) {
+       if (distance (grid.begin(), grid.end()) == 0) {
                return;
        }
 
-       i = end;
+       i = grid.end();
        i--;
-       if ((*i).beat >= (*begin).beat) {
-               bbt_bars = (*i).bar - (*begin).bar;
+
+       /* XX ?? */
+       if ((*i).beat >= (*grid.begin()).beat) {
+               bbt_bars = (*i).bar - (*grid.begin()).bar;
        } else {
-               bbt_bars = (*i).bar - (*begin).bar - 1;
+               bbt_bars = (*i).bar - (*grid.begin()).bar;
        }
-       beats = distance (begin, end) - bbt_bars;
+
+       beats = distance (grid.begin(), grid.end()) - bbt_bars;
 
        /* Only show the bar helper if there aren't many bars on the screen */
        if ((bbt_bars < 2) || (beats < 5)) {
@@ -1140,7 +1140,7 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
                bbt_ruler_scale =  bbt_show_ticks_detail;
        }
 
-       if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
+       if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
                bbt_ruler_scale =  bbt_show_ticks_super_detail;
        }
 }
@@ -1161,38 +1161,34 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                return;
        }
 
-       TempoMap::BBTPointList::const_iterator i;
+       std::vector<TempoMap::BBTPoint>::const_iterator i;
 
        char buf[64];
        gint  n = 0;
        framepos_t pos;
        Timecode::BBT_Time next_beat;
-       framepos_t next_beat_pos;
        uint32_t beats = 0;
        uint32_t tick = 0;
        uint32_t skip;
        uint32_t t;
-       framepos_t frame_skip;
-       double frame_skip_error;
        double bbt_position_of_helper;
-       double accumulated_error;
        bool i_am_accented = false;
        bool helper_active = false;
        ArdourCanvas::Ruler::Mark mark;
 
-       ARDOUR::TempoMap::BBTPointList::const_iterator begin;
-       ARDOUR::TempoMap::BBTPointList::const_iterator end;
+       std::vector<TempoMap::BBTPoint> grid;
 
-       compute_current_bbt_points (lower, upper, begin, end);
+       compute_current_bbt_points (grid, lower, upper);
 
-       if (distance (begin, end) == 0) {
+       if (distance (grid.begin(), grid.end()) == 0) {
                return;
        }
 
        switch (bbt_ruler_scale) {
 
        case bbt_show_beats:
-               beats = distance (begin, end);
+
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = beats + 2;
 
                mark.label = "";
@@ -1200,7 +1196,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                marks.push_back (mark);
 
-               for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
@@ -1228,7 +1224,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_ticks:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
                bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
@@ -1240,7 +1236,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                marks.push_back (mark);
 
-               for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
@@ -1265,45 +1261,20 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                        }
 
                        /* Add the tick marks */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
+                       tick = skip; // the first non-beat tick
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       /* Find the next beat */
-                       next_beat.beats = (*i).beat;
-                       next_beat.bars = (*i).bar;
-                       next_beat.ticks = 0;
-
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
+                               next_beat.beats = (*i).beat;
+                               next_beat.bars = (*i).bar;
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_time (next_beat);
 
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
-
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
-
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
                                        i_am_accented = true;
                                }
-
                                mark.label = "";
-
-                               /* Error compensation for float to framepos_t*/
-                               accumulated_error += frame_skip_error;
-                               if (accumulated_error > 1) {
-                                       pos += 1;
-                                       accumulated_error -= 1.0f;
-                               }
-
                                mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
@@ -1313,7 +1284,10 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                                }
                                i_am_accented = false;
                                marks.push_back (mark);
-                               n++;
+
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
@@ -1321,17 +1295,17 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_ticks_detail:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
-               bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
+               bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
 
                mark.label = "";
                mark.position = lower;
                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                marks.push_back (mark);
 
-               for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
@@ -1356,36 +1330,20 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                        }
 
                        /* Add the tick marks */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
+                       tick = skip; // the first non-beat tick
 
-                       /* Find the next beat */
-
-                       next_beat.beats = (*i).beat;
-                       next_beat.bars = (*i).bar;
-
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
-
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
+                               next_beat.beats = (*i).beat;
+                               next_beat.bars = (*i).bar;
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_time (next_beat);
 
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
-                                       i_am_accented = true;
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                                       i_am_accented = true;
                                }
-
                                if (i_am_accented && (pos > bbt_position_of_helper)){
                                        snprintf (buf, sizeof(buf), "%" PRIu32, tick);
                                } else {
@@ -1393,14 +1351,6 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                                }
 
                                mark.label = buf;
-
-                               /* Error compensation for float to framepos_t*/
-                               accumulated_error += frame_skip_error;
-                               if (accumulated_error > 1) {
-                                       pos += 1;
-                                       accumulated_error -= 1.0f;
-                               }
-
                                mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
@@ -1409,7 +1359,11 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                                        mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                }
                                i_am_accented = false;
-                               n++;
+                               marks.push_back (mark);
+
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
@@ -1417,17 +1371,17 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_ticks_super_detail:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
-               bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
+               bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
 
                mark.label = "";
                mark.position = lower;
                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                marks.push_back (mark);
 
-               for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
@@ -1452,61 +1406,40 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
                        }
 
                        /* Add the tick marks */
-
-                       /* Find the next beat */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
 
                        next_beat.beats = (*i).beat;
                        next_beat.bars = (*i).bar;
+                       tick = skip; // the first non-beat tick
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
-
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
-
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
-
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
-                                         i_am_accented = true;
-                                 }
-
-                                 if (pos > bbt_position_of_helper) {
-                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
-                                 } else {
-                                         buf[0] = '\0';
-                                 }
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_time (next_beat);
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                                       i_am_accented = true;
+                               }
 
-                                 mark.label = buf;
+                               if (pos > bbt_position_of_helper) {
+                                       snprintf (buf, sizeof(buf), "%" PRIu32, tick);
+                               } else {
+                                       buf[0] = '\0';
+                               }
 
-                                 /* Error compensation for float to framepos_t*/
-                                 accumulated_error += frame_skip_error;
-                                 if (accumulated_error > 1) {
-                                         pos += 1;
-                                         accumulated_error -= 1.0f;
-                                 }
+                               mark.label = buf;
+                               mark.position = pos;
 
-                                 mark.position = pos;
+                               if ((bbt_beat_subdivision > 4) && i_am_accented) {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                               } else {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               }
+                               i_am_accented = false;
+                               marks.push_back (mark);
 
-                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
-                                 } else {
-                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
-                                 }
-                                 i_am_accented = false;
-                                 marks.push_back (mark);
-                                 n++;
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
@@ -1523,7 +1456,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_64:
                        bbt_nmarks = (gint) (bbt_bars / 64) + 1;
-                       for (n = 0,   i = begin; i != end && n < bbt_nmarks; i++) {
+                       for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
                                if ((*i).is_bar()) {
                                        if ((*i).bar % 64 == 1) {
                                                if ((*i).bar % 256 == 1) {
@@ -1548,7 +1481,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_16:
                        bbt_nmarks = (bbt_bars / 16) + 1;
-               for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
+               for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 16 == 1) {
                                if ((*i).bar % 64 == 1) {
@@ -1573,7 +1506,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
 
        case bbt_show_4:
                bbt_nmarks = (bbt_bars / 4) + 1;
-               for (n = 0,   i = begin; i != end && n < bbt_nmarks; ++i) {
+               for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                if ((*i).bar % 16 == 1) {
@@ -1599,7 +1532,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l
        case bbt_show_1:
 //     default:
                bbt_nmarks = bbt_bars + 2;
-               for (n = 0,  i = begin; i != end && n < bbt_nmarks; ++i) {
+               for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
index 38685e5dd6983bc9bb3d6e59341ed0ddebd1a43e..2d5a3e47d5d94295d47b7265a46b5080f0626d85 100644 (file)
@@ -114,13 +114,12 @@ Editor::tempo_map_changed (const PropertyChange& /*ignored*/)
                tempo_lines->tempo_map_changed();
        }
 
-       ARDOUR::TempoMap::BBTPointList::const_iterator begin;
-       ARDOUR::TempoMap::BBTPointList::const_iterator end;
+       std::vector<TempoMap::BBTPoint> grid;
 
-       compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end);
+       compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
        _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
-       draw_measures (begin, end);
-       update_tempo_based_rulers (begin, end);
+       draw_measures (grid);
+       update_tempo_based_rulers (grid);
 }
 
 void
@@ -131,32 +130,31 @@ Editor::redisplay_tempo (bool immediate_redraw)
        }
 
        if (immediate_redraw) {
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin;
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end;
+               std::vector<TempoMap::BBTPoint> grid;
 
-               compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(),
-                                           current_bbt_points_begin, current_bbt_points_end);
-               draw_measures (current_bbt_points_begin, current_bbt_points_end);
-               update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); // redraw rulers and measures
+               compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
+               draw_measures (grid);
+               update_tempo_based_rulers (grid); // redraw rulers and measure lines
 
        } else {
                Glib::signal_idle().connect (sigc::bind_return (sigc::bind (sigc::mem_fun (*this, &Editor::redisplay_tempo), true), false));
        }
 }
 
+/* computes a grid starting a beat before and ending a beat after leftmost and rightmost respectively */
 void
-Editor::compute_current_bbt_points (framepos_t leftmost, framepos_t rightmost,
-                                   ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                                   ARDOUR::TempoMap::BBTPointList::const_iterator& end)
+Editor::compute_current_bbt_points (std::vector<TempoMap::BBTPoint>& grid, framepos_t leftmost, framepos_t rightmost)
 {
        if (!_session) {
                return;
        }
 
+       framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (leftmost)));
+       framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (rightmost)) + 1.0);
+
        /* prevent negative values of leftmost from creeping into tempomap
         */
-
-       _session->tempo_map().get_grid (begin, end, max (leftmost, (framepos_t) 0), rightmost);
+       _session->tempo_map().get_grid (grid, max (beat_before_lower_pos, (framepos_t) 0), beat_after_upper_pos);
 }
 
 void
@@ -168,10 +166,9 @@ Editor::hide_measures ()
 }
 
 void
-Editor::draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                      ARDOUR::TempoMap::BBTPointList::const_iterator& end)
+Editor::draw_measures (std::vector<ARDOUR::TempoMap::BBTPoint>& grid)
 {
-       if (_session == 0 || _show_measures == false || distance (begin, end) == 0) {
+       if (_session == 0 || _show_measures == false || distance (grid.begin(), grid.end()) == 0) {
                return;
        }
 
@@ -180,7 +177,7 @@ Editor::draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
        }
 
        const unsigned divisions = get_grid_beat_divisions(leftmost_frame);
-       tempo_lines->draw (begin, end, divisions, leftmost_frame, _session->frame_rate());
+       tempo_lines->draw (grid, divisions, leftmost_frame, _session->frame_rate());
 }
 
 void
@@ -214,7 +211,7 @@ Editor::mouse_add_new_tempo_event (framepos_t frame)
 
        begin_reversible_command (_("add tempo mark"));
         XMLNode &before = map.get_state();
-       map.add_tempo (Tempo (bpm,nt), requested);
+       map.add_tempo (Tempo (bpm,nt), requested, tempo_dialog.get_tempo_type());
         XMLNode &after = map.get_state();
        _session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
        commit_reversible_command ();
@@ -329,7 +326,7 @@ Editor::edit_tempo_section (TempoSection* section)
 
        begin_reversible_command (_("replace tempo mark"));
        XMLNode &before = _session->tempo_map().get_state();
-       _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when);
+       _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when, tempo_dialog.get_tempo_type());
        XMLNode &after = _session->tempo_map().get_state();
        _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
        commit_reversible_command ();
index a7a6f4ae759e90d00b535404a748d1c01ff0325b..0d6a6cc75edc26867fd2bcedb6e08eceb741ade4 100644 (file)
@@ -106,12 +106,21 @@ TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type,
                pulse_selector.set_active_text (strings[3]); // "quarter"
        }
 
+       strings.clear();
+
+       tempo_types.insert (make_pair (_("ramped"), TempoSection::TempoSectionType::Ramp));
+       strings.push_back (_("ramped"));
+       tempo_types.insert (make_pair (_("constant"), TempoSection::TempoSectionType::Constant));
+       strings.push_back (_("constant"));
+       set_popdown_strings (tempo_type, strings);
+       tempo_type.set_active_text (strings[0]); // "ramped"
+
        Table* table;
 
        if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
-               table = manage (new Table (5, 5));
+               table = manage (new Table (5, 6));
        } else {
-               table = manage (new Table (5, 4));
+               table = manage (new Table (5, 5));
        }
 
        table->set_spacings (6);
@@ -156,8 +165,12 @@ TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type,
                table->attach (*when_label, 0, 1, row, row+1);
        }
 
+       Label* tempo_type_label = manage (new Label(_("Tempo Type:"), ALIGN_LEFT, ALIGN_CENTER));
+       table->attach (*tempo_type_label, 0, 1, row+1, row+2);
+       table->attach (tempo_type, 1, 2, row+1, row + 2);
        get_vbox()->set_border_width (12);
        get_vbox()->pack_end (*table);
+
        table->show_all ();
 
        add_button (Stock::CANCEL, RESPONSE_CANCEL);
@@ -258,6 +271,19 @@ TempoDialog::get_note_type ()
        return x->second;
 }
 
+TempoSection::TempoSectionType
+TempoDialog::get_tempo_type ()
+{
+       TempoTypes::iterator x = tempo_types.find (tempo_type.get_active_text());
+
+       if (x == tempo_types.end()) {
+               error << string_compose(_("incomprehensible pulse note type (%1)"), tempo_type.get_active_text()) << endmsg;
+               return TempoSection::TempoSectionType::Constant;
+       }
+
+       return x->second;
+}
+
 void
 TempoDialog::pulse_change ()
 {
index 06c5db196da0545aaff24a39b890a412fb7e2642..f04a5417f7d1e24ced5796fe142519ae38a68c2d 100644 (file)
@@ -44,6 +44,7 @@ public:
        double get_bpm ();
        double get_note_type ();
        bool   get_bbt_time (Timecode::BBT_Time&);
+       ARDOUR::TempoSection::TempoSectionType get_tempo_type ();
 
 private:
        void init (const Timecode::BBT_Time& start, double, double, bool);
@@ -59,6 +60,9 @@ private:
        typedef std::map<std::string,float> NoteTypes;
        NoteTypes note_types;
 
+       typedef std::map<std::string, ARDOUR::TempoSection::TempoSectionType> TempoTypes;
+       TempoTypes tempo_types;
+
        bool tapped;      // whether the tap-tempo button has been clicked
        double sum_x, sum_xx, sum_xy, sum_y;
        double tap_count;
@@ -74,6 +78,8 @@ private:
        Gtk::Label   when_beat_label;
        Gtk::Label   pulse_selector_label;
        Gtk::Button  tap_tempo_button;
+       Gtk::ComboBoxText tempo_type;
+
 };
 
 class MeterDialog : public ArdourDialog
index bb86011458427b6a2004de2f352d5b0d14192e2c..c32048610852970a6c3e0a7b3396b8c72de7937e 100644 (file)
@@ -54,12 +54,12 @@ TempoLines::hide ()
 }
 
 void
-TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b,
-                        unsigned                                              divisions,
+TempoLines::draw_ticks (std::vector<ARDOUR::TempoMap::BBTPoint>& grid,
+                       unsigned                                              divisions,
                         framecnt_t                                            leftmost_frame,
                         framecnt_t                                            frame_rate)
 {
-       const double   fpb  = b->tempo->frames_per_beat(frame_rate);
+       const double   fpb  = grid.begin()->tempo->frames_per_beat(frame_rate);
        const uint32_t base = UIConfiguration::instance().color_mod("measure line beat", "measure line beat");
 
        for (unsigned l = 1; l < divisions; ++l) {
@@ -74,7 +74,8 @@ TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b,
                /* draw line with alpha corresponding to coarsest level */
                const uint8_t    a = max(8, (int)rint(UINT_RGBA_A(base) / (0.8 * log2(level))));
                const uint32_t   c = UINT_RGBA_CHANGE_A(base, a);
-               const framepos_t f = b->frame + (l * (fpb / (double)divisions));
+               const framepos_t f = grid.begin()->frame + (l * (fpb / (double)divisions));
+               //const framepos_t f = frame_at_tick (last_beat_in_ticks + (l * (BBT_Time::ticks_per_beat / (double)divisions)));
                if (f > leftmost_frame) {
                        lines.add (PublicEditor::instance().sample_to_pixel_unrounded (f), 1.0, c);
                }
@@ -82,13 +83,12 @@ TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b,
 }
 
 void
-TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                  const ARDOUR::TempoMap::BBTPointList::const_iterator& end,
-                  unsigned                                              divisions,
+TempoLines::draw (std::vector<ARDOUR::TempoMap::BBTPoint>& grid,
+                 unsigned                                              divisions,
                   framecnt_t                                            leftmost_frame,
                   framecnt_t                                            frame_rate)
 {
-       ARDOUR::TempoMap::BBTPointList::const_iterator i;
+       std::vector<ARDOUR::TempoMap::BBTPoint>::const_iterator i;
        double  beat_density;
 
        uint32_t beats = 0;
@@ -97,10 +97,10 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
 
        /* get the first bar spacing */
 
-       i = end;
+       i = grid.end();
        i--;
-       bars = (*i).bar - (*begin).bar;
-       beats = distance (begin, end) - bars;
+       bars = (*i).bar - (*grid.begin()).bar;
+       beats = distance (grid.begin(), grid.end()) - bars;
 
        beat_density = (beats * 10.0f) / lines.canvas()->width();
 
@@ -116,15 +116,14 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
        }
 
        lines.clear ();
-
-       if (beat_density <= 0.12 && begin != end && begin->frame > 0) {
-               /* draw subdivisions of the beat before the first visible beat line */
-               ARDOUR::TempoMap::BBTPointList::const_iterator prev = begin;
-               --prev;
-               draw_ticks(prev, divisions, leftmost_frame, frame_rate);
+       if (beat_density <= 0.12 && grid.begin() != grid.end() && grid.begin()->frame > 0) {
+               /* draw subdivisions of the beat before the first visible beat line XX this shouldn't happen now */
+               std::vector<ARDOUR::TempoMap::BBTPoint> vec;
+               vec.push_back (*i);
+               draw_ticks (vec, divisions, leftmost_frame, frame_rate);
        }
 
-       for (i = begin; i != end; ++i) {
+       for (i = grid.begin(); i != grid.end(); ++i) {
 
                if ((*i).is_bar()) {
                        color = UIConfiguration::instance().color ("measure line bar");
@@ -141,7 +140,10 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
 
                if (beat_density <= 0.12) {
                        /* draw subdivisions of this beat */
-                       draw_ticks(i, divisions, leftmost_frame, frame_rate);
+                       std::vector<ARDOUR::TempoMap::BBTPoint> vec;
+                       vec.push_back (*i);
+
+                       draw_ticks(vec, divisions, leftmost_frame, frame_rate);
                }
        }
 }
index 6d40a2d1a766923da70040183b3a34eacde75730..709602898177a39b0077d81e8d9c239854a75d40 100644 (file)
@@ -29,9 +29,8 @@ public:
 
        void tempo_map_changed();
 
-       void draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                  const ARDOUR::TempoMap::BBTPointList::const_iterator& end,
-                  unsigned                                              divisions,
+       void draw (std::vector<ARDOUR::TempoMap::BBTPoint>& grid,
+                  unsigned                                              divisions,
                   ARDOUR::framecnt_t                                    leftmost_frame,
                   ARDOUR::framecnt_t                                    frame_rate);
 
@@ -39,8 +38,8 @@ public:
        void hide();
 
 private:
-       void draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b,
-                        unsigned                                              divisions,
+       void draw_ticks (std::vector<ARDOUR::TempoMap::BBTPoint>& grid,
+                        unsigned                                              divisions,
                         ARDOUR::framecnt_t                                    leftmost_frame,
                         ARDOUR::framecnt_t                                    frame_rate);
 
index 1b43ff8c8cfb34a9d92b0ad006817eebeb66c962..9741d34adbca995d2ce1fae7fd58e1175aaa43ca 100644 (file)
@@ -55,6 +55,7 @@ class LIBARDOUR_API Tempo {
                : _beats_per_minute (bpm), _note_type(type) {}
 
        double beats_per_minute () const { return _beats_per_minute;}
+       double ticks_per_minute () const { return _beats_per_minute * Timecode::BBT_Time::ticks_per_beat;}
        double note_type () const { return _note_type;}
        /** audio samples per beat
         * @param sr samplerate
@@ -146,10 +147,15 @@ class LIBARDOUR_API MeterSection : public MetricSection, public Meter {
 /** A section of timeline with a certain Tempo. */
 class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
   public:
-       TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type)
-               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0)  {}
-       TempoSection (framepos_t start, double qpm, double note_type)
-               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0) {}
+       enum TempoSectionType {
+               Ramp,
+               Constant,
+       };
+
+       TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type, TempoSectionType tempo_type)
+               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type)  {}
+       TempoSection (framepos_t start, double qpm, double note_type, TempoSectionType tempo_type)
+               : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {}
        TempoSection (const XMLNode&);
 
        static const std::string xml_state_node_name;
@@ -160,7 +166,41 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        void update_bbt_time_from_bar_offset (const Meter&);
        double bar_offset() const { return _bar_offset; }
 
+       void set_type (TempoSectionType type);
+       TempoSectionType type () const { return _type; }
+
+       double tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+
+       double tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+
+       double beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+
   private:
+
+       framecnt_t minute_to_frame (double time, framecnt_t frame_rate) const;
+       double frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const;
+
+       /*  tempo ramp functions. zero-based with time in minutes,
+        * 'tick tempo' in ticks per minute and tempo in bpm.
+        *  time relative to section start.
+        */
+       double c_func (double end_tpm, double end_time) const;
+       double a_func (double begin_tpm, double end_tpm, double end_time) const;
+
+       double tempo_at_time (double time, double end_bpm, double end_time) const;
+       double time_at_tempo (double tempo, double end_bpm, double end_time) const;
+       double tick_tempo_at_time (double time, double end_tpm, double end_time) const;
+       double time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const;
+
+       double tick_at_time (double time, double end_tpm, double end_time) const;
+       double time_at_tick (double tick, double end_tpm, double end_time) const;
+
+       double beat_at_time (double time, double end_tpm, double end_time) const;
+       double time_at_beat (double beat, double end_tpm, double end_time) const;
+
        /* this value provides a fractional offset into the bar in which
           the tempo section is located in. A value of 0.0 indicates that
           it occurs on the first beat of the bar, a value of 0.5 indicates
@@ -170,6 +210,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
           position within the bar if/when the meter changes.
        */
        double _bar_offset;
+       TempoSectionType _type;
 };
 
 typedef std::list<MetricSection*> Metrics;
@@ -231,11 +272,11 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
        struct BBTPoint {
                framepos_t          frame;
                const MeterSection* meter;
-               const TempoSection* tempo;
+               const Tempo* tempo;
                uint32_t            bar;
                uint32_t            beat;
 
-               BBTPoint (const MeterSection& m, const TempoSection& t, framepos_t f,
+               BBTPoint (const MeterSection& m, const Tempo& t, framepos_t f,
                          uint32_t b, uint32_t e)
                        : frame (f), meter (&m), tempo (&t), bar (b), beat (e) {}
 
@@ -245,19 +286,18 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
                bool is_bar() const { return beat == 1; }
        };
 
-       typedef std::vector<BBTPoint> BBTPointList;
-
        template<class T> void apply_with_metrics (T& obj, void (T::*method)(const Metrics&)) {
                Glib::Threads::RWLock::ReaderLock lm (lock);
                (obj.*method)(metrics);
        }
 
-       void get_grid (BBTPointList::const_iterator&, BBTPointList::const_iterator&,
+       void get_grid (std::vector<BBTPoint>&,
                       framepos_t start, framepos_t end);
 
        /* TEMPO- AND METER-SENSITIVE FUNCTIONS
 
-          bbt_time(), bbt_time_rt(), frame_time() and bbt_duration_at()
+          bbt_time(), beat_at_frame(), frame_at_beat(), tick_at_frame(),
+          frame_at_tick(),frame_time() and bbt_duration_at()
           are all sensitive to tempo and meter, and will give answers
           that align with the grid formed by tempo and meter sections.
 
@@ -267,11 +307,12 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
 
        void bbt_time (framepos_t when, Timecode::BBT_Time&);
 
-       /* realtime safe variant of ::bbt_time(), will throw
-          std::logic_error if the map is not large enough
-          to provide an answer.
-       */
-       void       bbt_time_rt (framepos_t when, Timecode::BBT_Time&);
+       double tick_at_frame (framecnt_t frame) const;
+       framecnt_t frame_at_tick (double tick) const;
+
+       double beat_at_frame (framecnt_t frame) const;
+       framecnt_t frame_at_beat (double beat) const;
+
        framepos_t frame_time (const Timecode::BBT_Time&);
        framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir);
 
@@ -293,19 +334,19 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
        static const Tempo& default_tempo() { return _default_tempo; }
        static const Meter& default_meter() { return _default_meter; }
 
-       const Tempo& tempo_at (framepos_t) const;
+       const Tempo tempo_at (framepos_t) const;
        const Meter& meter_at (framepos_t) const;
 
        const TempoSection& tempo_section_at (framepos_t) const;
        const MeterSection& meter_section_at (framepos_t) const;
 
-       void add_tempo (const Tempo&, Timecode::BBT_Time where);
+       void add_tempo (const Tempo&, Timecode::BBT_Time where, TempoSection::TempoSectionType type);
        void add_meter (const Meter&, Timecode::BBT_Time where);
 
        void remove_tempo (const TempoSection&, bool send_signal);
        void remove_meter (const MeterSection&, bool send_signal);
 
-       void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where);
+       void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where, TempoSection::TempoSectionType type);
        void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where);
 
        framepos_t round_to_bar  (framepos_t frame, RoundMode dir);
@@ -352,32 +393,26 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
        Metrics                       metrics;
        framecnt_t                    _frame_rate;
        mutable Glib::Threads::RWLock lock;
-       BBTPointList                  _map;
 
        void recompute_map (bool reassign_tempo_bbt, framepos_t end = -1);
-       void extend_map (framepos_t end);
-       void require_map_to (framepos_t pos);
-       void require_map_to (const Timecode::BBT_Time&);
+
        void _extend_map (TempoSection* tempo, MeterSection* meter,
                          Metrics::iterator next_metric,
-                         Timecode::BBT_Time current, framepos_t current_frame, framepos_t end);
-
-       BBTPointList::const_iterator bbt_before_or_at (framepos_t);
-       BBTPointList::const_iterator bbt_before_or_at (const Timecode::BBT_Time&);
-       BBTPointList::const_iterator bbt_after_or_at (framepos_t);
+                         Timecode::BBT_Time current, framepos_t current_frame, framepos_t end);
 
        framepos_t round_to_type (framepos_t fr, RoundMode dir, BBTPointType);
-       void bbt_time (framepos_t, Timecode::BBT_Time&, const BBTPointList::const_iterator&);
-       framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir);
 
        const MeterSection& first_meter() const;
        MeterSection&       first_meter();
        const TempoSection& first_tempo() const;
        TempoSection&       first_tempo();
 
+       Timecode::BBT_Time beats_to_bbt (double beats);
+       int32_t bars_in_meter_section (MeterSection* ms) const;
+
        void do_insert (MetricSection* section);
 
-       void add_tempo_locked (const Tempo&, Timecode::BBT_Time where, bool recompute);
+       void add_tempo_locked (const Tempo&, Timecode::BBT_Time where, bool recompute, TempoSection::TempoSectionType type);
        void add_meter_locked (const Meter&, Timecode::BBT_Time where, bool recompute);
 
        bool remove_tempo_locked (const TempoSection&);
index 4cb556c8a60d8c9b34aab882df92884cde02aea7..872828e57ecf64df2e481f2f31865508d0dfa9ae 100644 (file)
@@ -42,8 +42,7 @@ Pool Click::pool ("click", sizeof (Click), 1024);
 void
 Session::click (framepos_t start, framecnt_t nframes)
 {
-       TempoMap::BBTPointList::const_iterator points_begin;
-       TempoMap::BBTPointList::const_iterator points_end;
+       vector<TempoMap::BBTPoint> points;
        Sample *buf;
        framecnt_t click_distance;
 
@@ -72,13 +71,13 @@ Session::click (framepos_t start, framecnt_t nframes)
        BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1));
        buf = bufs.get_audio(0).data();
 
-       _tempo_map->get_grid (points_begin, points_end, start, end);
+       _tempo_map->get_grid (points, start, end);
 
-       if (distance (points_begin, points_end) == 0) {
+       if (distance (points.begin(), points.end()) == 0) {
                goto run_clicks;
        }
 
-       for (TempoMap::BBTPointList::const_iterator i = points_begin; i != points_end; ++i) {
+       for (vector<TempoMap::BBTPoint>::iterator i = points.begin(); i != points.end(); ++i) {
                switch ((*i).beat) {
                case 1:
                        if (click_emphasis_data && Config->get_use_click_emphasis () == true) {
index 21875ece4654c41b6b194f023248bb831a2958ca..34df6bd52e55e4585900699f7e96c8dd4f71925e 100644 (file)
@@ -223,9 +223,29 @@ intptr_t Session::vst_callback (
                                Timecode::BBT_Time bbt;
 
                                try {
-                                       session->tempo_map().bbt_time_rt (now, bbt);
-                                       double ppqBar;
-                                       double ppqPos = vst_ppq (tm, bbt, ppqBar);
+                                       session->tempo_map().bbt_time (now, bbt);
+
+                                       /* PPQ = pulse per quarter
+                                        * VST's "pulse" is our "division".
+                                        *
+                                        * 8 divisions per bar, 1 division = quarter, so 8 quarters per bar, ppq = 1
+                                        * 8 divisions per bar, 1 division = eighth, so  4 quarters per bar, ppq = 2
+                                        * 4 divisions per bar, 1 division = quarter, so  4 quarters per bar, ppq = 1
+                                        * 4 divisions per bar, 1 division = half, so 8 quarters per bar, ppq = 0.5
+                                        * 4 divisions per bar, 1 division = fifth, so (4 * 5/4) quarters per bar, ppq = 5/4
+                                        *
+                                        * general: divs_per_bar / (note_type / 4.0)
+                                        */
+                                       double ppq_scaling =  tm.meter().note_divisor() / 4.0;
+
+                                       /* Note that this assumes constant meter/tempo throughout the session. Stupid VST */
+                                       double ppqBar = double(bbt.bars - 1) * tm.meter().divisions_per_bar();
+                                       double ppqBeat = double(bbt.beats - 1);
+                                       double ppqTick = double(bbt.ticks) / Timecode::BBT_Time::ticks_per_beat;
+
+                                       ppqBar *= ppq_scaling;
+                                       ppqBeat *= ppq_scaling;
+                                       ppqTick *= ppq_scaling;
 
                                        if (value & (kVstPpqPosValid)) {
                                                timeinfo->ppqPos = ppqPos;
index 445fc7bab38546a959c843c2fc4ece75379fd4a9..383abfa3afe68ba7366ede5d1b572400e0e150db 100644 (file)
@@ -126,6 +126,16 @@ TempoSection::TempoSection (const XMLNode& node)
                        throw failed_constructor();
                }
        }
+
+       if ((prop = node.property ("tempo-type")) == 0) {
+               _type = TempoSectionType::Ramp;
+       } else {
+               if (strstr(prop->value().c_str(),"Constant")) {
+                       _type = TempoSectionType::Constant;
+               } else {
+                       _type = TempoSectionType::Ramp;
+               }
+       }
 }
 
 XMLNode&
@@ -149,6 +159,9 @@ TempoSection::get_state() const
        snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
        root->add_property ("movable", buf);
 
+       snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
+       root->add_property ("tempo-type", buf);
+
        return *root;
 }
 
@@ -162,6 +175,143 @@ TempoSection::update_bar_offset_from_bbt (const Meter& m)
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
 }
 
+void
+TempoSection::set_type (TempoSectionType type)
+{
+       _type = type;
+}
+
+double
+TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+
+       if (_type == Constant) {
+               return beats_per_minute();
+       }
+       return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
+}
+
+framepos_t
+TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return 0;
+       }
+
+       return minute_to_frame (time_at_tick_tempo (tempo *  BBT_Time::ticks_per_beat,  end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
+}
+
+double
+TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return frame / frames_per_beat (frame_rate);
+       }
+
+       return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
+}
+
+framepos_t
+TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       if (_type == Constant) {
+               return (framepos_t) floor (tick * frames_per_beat(frame_rate));
+       }
+
+       return minute_to_frame (time_at_tick (tick, end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
+}
+
+double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
+}
+
+framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
+}
+
+framecnt_t
+TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+{
+       return time * 60.0 * frame_rate;
+}
+
+double
+TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
+{
+       return (frame / (double) frame_rate) / 60.0;
+}
+
+/* constant for exp */
+double
+TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
+{
+       return log (end_tpm / ticks_per_minute()) /  c_func (end_tpm, end_time);
+}
+double
+TempoSection::c_func (double end_tpm, double end_time) const
+{
+       return log (end_tpm / ticks_per_minute()) /  end_time;
+}
+
+/* tempo in tpm at time in minutes */
+double
+TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
+{
+       return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
+}
+
+/* time in minutes at tempo in tpm */
+double
+TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
+{
+       return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
+}
+
+/* tempo in bpm at time in minutes */
+double
+TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
+{
+       return tick_tempo_at_time (time, end_bpm *  BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
+}
+
+/* time in minutes at tempo in bpm */
+double
+TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
+{
+       return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
+}
+
+/* tick at time in minutes */
+double
+TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
+{
+       return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
+}
+
+/* time in minutes at tick */
+double
+TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
+{
+       return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
+}
+
+/* beat at time in minutes */
+double
+TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
+{
+       return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
+}
+
+/* time in munutes at beat */
+double
+TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
+{
+       return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
+}
+
 void
 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 {
@@ -180,7 +330,6 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
 
        /* remember the 1-based counting properties of beats */
        new_start.beats += 1;
-
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
                                                       _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
 
@@ -284,8 +433,7 @@ TempoMap::TempoMap (framecnt_t fr)
        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());
+       TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::TempoSectionType::Ramp);
        MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
 
        t->set_movable (false);
@@ -489,15 +637,16 @@ TempoMap::do_insert (MetricSection* section)
 }
 
 void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::TempoSectionType type)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
+               TempoSection::TempoSectionType tt = first.type();
 
                if (ts.start() != first.start()) {
                        remove_tempo_locked (ts);
-                       add_tempo_locked (tempo, where, true);
+                       add_tempo_locked (tempo, where, true, tt);
                } else {
                        {
                                /* cannot move the first tempo section */
@@ -511,11 +660,11 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T
 }
 
 void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
+TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::TempoSectionType type)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
-               add_tempo_locked (tempo, where, true);
+               add_tempo_locked (tempo, where, true, type);
        }
 
 
@@ -523,12 +672,11 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
 }
 
 void
-TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute)
+TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::TempoSectionType type)
 {
        /* new tempos always start on a beat */
        where.ticks = 0;
-
-       TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
+       TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
 
        /* find the meter to use to set the bar offset of this
         * tempo section.
@@ -767,38 +915,6 @@ TempoMap::first_tempo ()
        return *t;
 }
 
-void
-TempoMap::require_map_to (framepos_t pos)
-{
-       Glib::Threads::RWLock::WriterLock lm (lock);
-
-       if (_map.empty() || _map.back().frame < pos) {
-               extend_map (pos);
-       }
-}
-
-void
-TempoMap::require_map_to (const BBT_Time& bbt)
-{
-       Glib::Threads::RWLock::WriterLock lm (lock);
-
-       /* since we have no idea where BBT is if its off the map, see the last
-        * point in the map is past BBT, and if not add an arbitrary amount of
-        * time until it is.
-        */
-
-       int additional_minutes = 1;
-
-       while (1) {
-               if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
-                       break;
-               }
-               /* add some more distance, using bigger steps each time */
-               extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
-               additional_minutes *= 2;
-       }
-}
-
 void
 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
 {
@@ -818,10 +934,12 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                end = max_framepos;
 
        } else {
+               /*
                if (!_map.empty ()) {
-                       /* never allow the map to be shortened */
+                       /* never allow the map to be shortened /
                        end = max (end, _map.back().frame);
                }
+               */
        }
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
@@ -857,7 +975,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
        current.bars = 1;
        current.beats = 1;
        current.ticks = 0;
-
        if (reassign_tempo_bbt) {
 
                MeterSection* rmeter = meter;
@@ -892,10 +1009,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
        ++next_metric; // skip meter (or tempo)
        ++next_metric; // skip tempo (or meter)
 
-       _map.clear ();
-
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
-       _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
 
        if (end == 0) {
                /* silly call from Session::process() during startup
@@ -906,48 +1020,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
        _extend_map (tempo, meter, next_metric, current, current_frame, end);
 }
 
-void
-TempoMap::extend_map (framepos_t end)
-{
-       /* CALLER MUST HOLD WRITE LOCK */
-
-       if (_map.empty()) {
-               recompute_map (false, end);
-               return;
-       }
-
-       BBTPointList::const_iterator i = _map.end();
-       Metrics::iterator next_metric;
-
-       --i;
-
-       BBT_Time last_metric_start;
-
-       if ((*i).tempo->frame() > (*i).meter->frame()) {
-               last_metric_start = (*i).tempo->start();
-       } else {
-               last_metric_start = (*i).meter->start();
-       }
-
-       /* find the metric immediately after the tempo + meter sections for the
-        * last point in the map
-        */
-
-       for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
-               if ((*next_metric)->start() > last_metric_start) {
-                       break;
-               }
-       }
-
-       /* we cast away const here because this is the one place where we need
-        * to actually modify the frame time of each metric section.
-        */
-
-       _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,
                       Metrics::iterator next_metric,
@@ -955,160 +1027,66 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
 {
        /* CALLER MUST HOLD WRITE LOCK */
 
-       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));
-
-       if (current.beats == 1) {
-               bar_start_frame = current_frame;
-       } else {
-               bar_start_frame = 0;
-       }
-
-       beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
-       current_frame_exact = current_frame;
-
-       while (current_frame < end) {
-
-               current.beats++;
-               current_frame_exact += beat_frames;
-               current_frame = llrint(current_frame_exact);
-
-               if (current.beats > meter->divisions_per_bar()) {
-                       current.bars++;
-                       current.beats = 1;
-               }
-
-               if (next_metric != metrics.end()) {
-
-                       /* no operator >= so invert operator < */
-
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
-
-                       if (!(current < (*next_metric)->start())) {
-
-               set_metrics:
-                               if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
+       uint32_t first_tick_in_new_meter = 0;
+       Metrics::const_iterator i;
+       TempoSection* prev_ts = tempo;
 
-                                       tempo = ts;
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-                                       /* new tempo section: if its on a beat,
-                                        * we don't have to do anything other
-                                        * than recompute various distances,
-                                        * done further below as we transition
-                                        * the next metric section.
-                                        *
-                                        * if its not on the beat, we have to
-                                        * compute the duration of the beat it
-                                        * 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 this new tempo.
-                                        */
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
 
-                                       if (tempo->start().ticks != 0) {
+                       if (m->start() >= prev_ts->start()) {
+                               first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter
+                               for (i = metrics.begin(); i != metrics.end(); ++i) {
+                                       TempoSection* t;
 
-                                               double next_beat_frames = tempo->frames_per_beat (_frame_rate);
+                                       if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-                                               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()));
+                                               if (t->start() >= m->start() && t->start() > prev_ts->start()) {
+                                                       //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
+                                                       //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
 
-                                               /* back up to previous beat */
-                                               current_frame_exact -= beat_frames;
-                                               current_frame = llrint(current_frame_exact);
+                                                       /*tempo section (t) lies in the previous meter */
+                                                       double ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) )  * BBT_Time::ticks_per_beat) + t->start().ticks;
 
-                                               /* set tempo section location
-                                                * based on offset from last
-                                                * bar start
-                                                */
-                                               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
-                                                * offset within the beat that
-                                                * would have been there
-                                                * without the tempo
-                                                * change. then stretch the
-                                                * beat accordingly.
-                                                */
+                                                       double ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1))  * BBT_Time::ticks_per_beat) + prev_ts->start().ticks;
 
-                                               double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
+                                                       double ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
+                                                       /* assume (falsely) that the target tempo is constant */
+                                                       double length_estimate = (ticks_relative_to_prev_ts /  BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
+                                                       double system_precision_at_target_tempo =  (_frame_rate / t->ticks_per_minute());
+                                                       cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
+                                                       double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
 
-                                               current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
-                                               current_frame = llrint(current_frame_exact);
+                                                       while (fabs (tick_error) >= system_precision_at_target_tempo) {
 
-                                               /* 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));
+                                                               double actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate);
+                                                               tick_error = ticks_relative_to_prev_ts - actual_ticks;
+                                                               length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
+                                                               cerr << "actual ticks = " << actual_ticks << endl;
 
-                                       } else {
+                                                               cerr << "tick error  = " << tick_error << endl;
+                                                       }
+                                                       t->set_frame (length_estimate + prev_ts->frame());
 
-                                               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);
+                                                       if (m->start() < t->start() && m->start() == prev_ts->start()) {
+                                                               m->set_frame (prev_ts->frame());
+                                                       } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
+                                                               m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate));
+                                                       }
+                                               }
+                                               prev_ts = t;
                                        }
-
-                               } 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",
-                                                                              beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
-
-                               ++next_metric;
-
-                               if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
-                                       /* same position so go back and set this one up before advancing
-                                        */
-                                       goto set_metrics;
                                }
-
-                       }
-               }
-
-               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, 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));
-                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
-               }
-
-               if (next_metric == metrics.end()) {
-                       /* no more metrics - we've timestamped them all, stop here */
-                       if (end == max_framepos) {
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
-                                                                              current.bars, current.beats, current_frame));
-                               break;
                        }
+                       meter = m;
                }
        }
 }
 
+
 TempoMetric
 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
 {
@@ -1168,8 +1146,6 @@ TempoMap::metric_at (BBT_Time bbt) const
 void
 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
 {
-       require_map_to (frame);
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
 
        if (frame < 0) {
@@ -1179,180 +1155,431 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
                warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
                return;
        }
-
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
+       bbt = beats_to_bbt (beat_at_frame (frame));
 }
 
-void
-TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
+int32_t
+TempoMap::bars_in_meter_section (MeterSection* ms) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+       /* YOU MUST HAVE THE READ LOCK */
+       Metrics::const_iterator i;
 
-       if (!lm.locked()) {
-               throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
-       }
+       MeterSection* next_ms = 0;
+       const MeterSection* prev_ms = &first_meter();
 
-       if (_map.empty() || _map.back().frame < frame) {
-               throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (ms->frame() < m->frame()) {
+                               next_ms = m;
+                               break;
+                       }
+                       prev_ms = m;
+               }
        }
+       if (next_ms) {
+               double ticks_at_next = tick_at_frame (next_ms->frame());
+               double ticks_at_prev = tick_at_frame (prev_ms->frame());
+               double ticks_in_meter = ticks_at_next - ticks_at_prev;
 
-       return bbt_time (frame, bbt, bbt_before_or_at (frame));
+               return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
+       }
+       return -1;
 }
 
-void
-TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
+Timecode::BBT_Time
+TempoMap::beats_to_bbt (double beats)
 {
-       /* CALLER MUST HOLD READ LOCK */
+       /* CALLER HOLDS READ LOCK */
+       BBT_Time ret;
+       MeterSection* prev_ms = &first_meter();
 
-       bbt.bars = (*i).bar;
-       bbt.beats = (*i).beat;
+       framecnt_t frame = frame_at_beat (beats);
+       uint32_t cnt = 0;
 
-       if ((*i).frame == frame) {
-               bbt.ticks = 0;
-       } else {
-               bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
-                                   BBT_Time::ticks_per_beat);
-       }
-}
+       if (n_meters() < 2) {
+               uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
+               double remaining_beats = beats - (bars *  prev_ms->note_divisor());
+               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
-framepos_t
-TempoMap::frame_time (const BBT_Time& bbt)
-{
-       if (bbt.bars < 1) {
-               warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
-               return 0;
-       }
+               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+               ret.beats = (uint32_t) floor (remaining_beats);
+               ret.bars = bars;
 
-       if (bbt.beats < 1) {
-               throw std::logic_error ("beats are counted from one");
+               /* 0 0 0 to 1 1 0 - based mapping*/
+               ++ret.bars;
+               ++ret.beats;
+
+               if (ret.ticks >= BBT_Time::ticks_per_beat) {
+                       ++ret.beats;
+                       ret.ticks -= BBT_Time::ticks_per_beat;
+               }
+
+               if (ret.beats > prev_ms->note_divisor()) {
+                       ++ret.bars;
+                       ret.beats = 1;
+               }
+
+               return ret;
        }
 
-       require_map_to (bbt);
+       uint32_t first_beat_in_meter = 0;
+       uint32_t accumulated_bars = 0;
+       Metrics::const_iterator i;
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m = 0;
 
-       BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
-       BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       first_beat_in_meter = beat_at_frame (m->frame());
 
-       if (bbt.ticks != 0) {
-               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);
+                       if (beats < first_beat_in_meter) {
+                               /* this is the meter after the one our beat is on*/
+                               break;
+                       }
+                       int32_t const bars_in_ms = bars_in_meter_section (m);
+
+                       if (bars_in_ms > 0) {
+                               accumulated_bars += bars_in_ms;
+                       }
+
+                       prev_ms = m;
+                       ++cnt;
+               }
        }
-}
+       //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter =  " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars <<  endl;
 
-framecnt_t
-TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
-{
-       BBT_Time when;
-       bbt_time (pos, when);
+       if (beats > first_beat_in_meter) {
+               /* prev_ms is the relevant one here */
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       return bbt_duration_at_unlocked (when, bbt, dir);
-}
+               /* now get the ticks at frame */
+               double ticks_at_frame = tick_at_frame (frame);
 
-framecnt_t
-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;
-       }
+               /* find the number of ticks at the beginning of the meter section (bar 1)*/
+               double ticks_at_ms = tick_at_frame (prev_ms->frame());
 
-       /* round back to the previous precise beat */
-       BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
-       BBTPointList::const_iterator start (wi);
+               double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
 
-       assert (wi != _map.end());
+               uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
+               double remaining_beats = beats_used_by_ms - (bars *  prev_ms->note_divisor());
+               double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
 
-       uint32_t bars = 0;
-       uint32_t beats = 0;
+               ret.bars = bars + accumulated_bars;
+               ret.beats = (uint32_t) floor (remaining_beats);
+               ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
 
-       while (wi != _map.end() && bars < bbt.bars) {
-               ++wi;
-               if ((*wi).is_bar()) {
-                       ++bars;
+               /* now ensure we srtart at 1 1 0 */
+               ++ret.bars;
+               ++ret.beats;
+               //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
+               if (ret.ticks >= BBT_Time::ticks_per_beat) {
+                       ++ret.beats;
+                       ret.ticks -= BBT_Time::ticks_per_beat;
+               }
+
+               if (ret.beats > prev_ms->note_divisor()) {
+                       ++ret.bars;
+                       ret.beats = 1;
                }
-       }
-       assert (wi != _map.end());
 
-       while (wi != _map.end() && beats < bbt.beats) {
-               ++wi;
-               ++beats;
+               return ret;
        }
-       assert (wi != _map.end());
 
-       /* add any additional frames related to ticks in the added value */
+       /* find the number of ticks at the beginning of the meter section (bar 1)*/
+       double ticks_at_ms = tick_at_frame (prev_ms->frame());
 
-       if (bbt.ticks != 0) {
-               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);
+       /* now get the ticks at frame */
+       double ticks_at_frame = tick_at_frame (frame);
+
+       double ticks_within_ms = ticks_at_frame - ticks_at_ms;
+
+       ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
+       uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
+       ret.beats = (uint32_t) floor (remaining_ticks);
+       remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
+
+       /* only round ticks */
+       ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+
+       /* now ensure we srtart at 1 1 0 */
+       ++ret.bars;
+       ++ret.beats;
+       if (ret.ticks >= BBT_Time::ticks_per_beat) {
+               ++ret.beats;
+               ret.ticks -= BBT_Time::ticks_per_beat;
        }
-}
 
-framepos_t
-TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
-{
-       return round_to_type (fr, dir, Bar);
-}
+       if (ret.beats > prev_ms->note_divisor()) {
+               ++ret.bars;
+               ret.beats = 1;
+       }
 
-framepos_t
-TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
-{
-       return round_to_type (fr, dir, Beat);
+       return ret;
 }
 
-framepos_t
-TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
+double
+TempoMap::tick_at_frame (framecnt_t frame) const
 {
-       require_map_to (fr);
+       Metrics::const_iterator i;
+       const TempoSection* prev_ts = &first_tempo();
+       double accumulated_ticks = 0.0;
+       uint32_t cnt = 0;
 
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       BBTPointList::const_iterator i = bbt_before_or_at (fr);
-       BBT_Time the_beat;
-       uint32_t ticks_one_subdivisions_worth;
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
 
-       bbt_time (fr, the_beat, i);
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
-       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
-                                                    fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
+                       if (frame < t->frame()) {
+                               /*the previous ts is the one containing the frame */
 
-       ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
+                               framepos_t time = frame - prev_ts->frame();
+                               framepos_t last_frame = t->frame() - prev_ts->frame();
+                               double last_beats_per_minute = t->beats_per_minute();
 
-       if (dir > 0) {
+                               return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
+                       }
 
-               /* round to next (or same iff dir == RoundUpMaybe) */
+                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
+                               framepos_t time = t->frame() - prev_ts->frame();
+                               framepos_t last_frame = t->frame() - prev_ts->frame();
+                               double last_beats_per_minute = t->beats_per_minute();
+                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
+                       }
 
-               uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
+                       prev_ts = t;
+                       ++cnt;
+               }
+       }
+
+       /* treated s linear for this ts */
+       framecnt_t frames_in_section = frame - prev_ts->frame();
+       double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+
+       return ticks_in_section + accumulated_ticks;
+
+}
+
+framecnt_t
+TempoMap::frame_at_tick (double tick) const
+{
+       double accumulated_ticks = 0.0;
+       const TempoSection* prev_ts =  &first_tempo();
+       uint32_t cnt = 0;
+
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
+                               framepos_t time = t->frame() - prev_ts->frame();
+                               framepos_t last_time = t->frame() - prev_ts->frame();
+                               double last_beats_per_minute = t->beats_per_minute();
+                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+                       }
+
+                       if (tick < accumulated_ticks) {
+                               /* prev_ts is the one affecting us. */
+
+                               double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
+                               framepos_t section_start = prev_ts->frame();
+                               framepos_t last_time = t->frame() - prev_ts->frame();
+                               double last_beats_per_minute = t->beats_per_minute();
+                               return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
+                       }
+
+                       prev_ts = t;
+                       ++cnt;
+               }
+       }
+       double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
+       double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
+       framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+
+       return ret;
+}
+
+double
+TempoMap::beat_at_frame (framecnt_t frame) const
+{
+       return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
+}
+
+framecnt_t
+TempoMap::frame_at_beat (double beat) const
+{
+       return frame_at_tick (beat * BBT_Time::ticks_per_beat);
+}
+
+framepos_t
+TempoMap::frame_time (const BBT_Time& bbt)
+{
+       if (bbt.bars < 1) {
+               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");
+       }
+
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       Metrics::const_iterator i;
+       uint32_t accumulated_bars = 0;
+
+       MeterSection* prev_ms = &first_meter();
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       int32_t const bims = bars_in_meter_section (m);
+
+                       if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
+                               break;
+                       }
+                       if (bims > 0 ) {
+                               accumulated_bars += bims;
+                       }
+                       prev_ms = m;
+               }
+       }
+
+       uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
+       double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
+       double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
+       double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
+
+       TempoSection* prev_ts = &first_tempo();
+       double accumulated_ticks = 0.0;
+       uint32_t cnt = 0;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (t->frame() < prev_ms->frame()) {
+                               continue;
+                       }
+
+                       if (cnt > 0 && t->frame() > prev_ts->frame()) {
+                               /*find the number of ticke in this section */
+                               framepos_t const time = t->frame() - prev_ts->frame();
+                               framepos_t const last_time = t->frame() - prev_ts->frame();
+                               double const last_beats_per_minute = t->beats_per_minute();
+                               accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+                       }
+
+                       if (ticks_target < accumulated_ticks) {
+                               double const ticks_in_section = ticks_target - tick_at_frame (prev_ts->frame());
+                               framepos_t const section_start_time = prev_ts->frame();
+                               framepos_t const last_time = t->frame() - prev_ts->frame();
+                               double const last_beats_per_minute = t->beats_per_minute();
+                               framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
+                               return ret;
+                       }
+
+                       prev_ts = t;
+                       ++cnt;
+               }
+       }
+
+       /*treat this ts as constant tempo */
+       double const ticks_in_this_ts = ticks_target - tick_at_frame (prev_ts->frame());
+       double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
+       framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+       return ret;
+}
+
+
+framecnt_t
+TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
+{
+
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       Metrics::const_iterator i;
+       TempoSection* first = 0;
+       TempoSection* second = 0;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+                       if ((*i)->frame() > pos) {
+                               second = t;
+                               break;
+                       }
+
+                       first = t;
+               }
+       }
+       if (first && second) {
+               framepos_t const last_time = second->frame() - first->frame();
+               double const last_beats_per_minute = second->beats_per_minute();
+
+               framepos_t const time = pos - first->frame();
+               double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+               double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
+
+               double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
+
+               return time_at_bbt - time;
+       }
+
+       double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
+       return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
+}
+
+framepos_t
+TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
+{
+       return round_to_type (fr, dir, Bar);
+}
+
+framepos_t
+TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
+{
+       return round_to_type (fr, dir, Beat);
+}
+
+framepos_t
+TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
+{
+       uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
+       uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
+       uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
+
+       ticks -= beats * BBT_Time::ticks_per_beat;
+
+       if (dir > 0) {
+               /* round to next (or same iff dir == RoundUpMaybe) */
+
+               uint32_t mod = ticks % ticks_one_subdivisions_worth;
 
                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;
+                       ticks += ticks_one_subdivisions_worth;
 
                } else {
                        /* not on subdivision, compute distance to next subdivision */
 
-                       the_beat.ticks += ticks_one_subdivisions_worth - mod;
+                       ticks += ticks_one_subdivisions_worth - mod;
                }
 
-               if (the_beat.ticks > BBT_Time::ticks_per_beat) {
-                       assert (i != _map.end());
-                       ++i;
-                       assert (i != _map.end());
-                       the_beat.ticks -= BBT_Time::ticks_per_beat;
+               if (ticks >= BBT_Time::ticks_per_beat) {
+                       ticks -= BBT_Time::ticks_per_beat;
                }
-
-
        } else if (dir < 0) {
 
                /* round to previous (or same iff dir == RoundDownMaybe) */
 
-               uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
+               uint32_t difference = ticks % ticks_one_subdivisions_worth;
 
                if (difference == 0 && dir == RoundDownAlways) {
                        /* right on the subdivision, but force-rounding down,
@@ -1360,236 +1587,136 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
                        difference = ticks_one_subdivisions_worth;
                }
 
-               if (the_beat.ticks < difference) {
-                       if (i == _map.begin()) {
-                               /* can't go backwards from wherever pos is, so just return it */
-                               return fr;
-                       }
-                       --i;
-                       the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
+               if (ticks < difference) {
+                       ticks = BBT_Time::ticks_per_beat - ticks;
                } else {
-                       the_beat.ticks -= difference;
+                       ticks -= difference;
                }
 
        } else {
                /* round to nearest */
-
                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) 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));
+                       ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
 
-                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
+                       DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
 
-                       if (the_beat.ticks > BBT_Time::ticks_per_beat) {
-                               assert (i != _map.end());
-                               ++i;
-                               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));
+                       if (ticks > BBT_Time::ticks_per_beat) {
+                               ++beats;
+                               ticks -= BBT_Time::ticks_per_beat;
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
                        }
 
                } else if (rem > 0) {
 
                        /* closer to previous subdivision, so shift backward */
 
-                       if (rem > the_beat.ticks) {
-                               if (i == _map.begin()) {
+                       if (rem > ticks) {
+                               if (beats == 0) {
                                        /* can't go backwards past zero, so ... */
                                        return 0;
                                }
                                /* step back to previous beat */
-                               --i;
-                               the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
+                               --beats;
+                               ticks = lrint (BBT_Time::ticks_per_beat - rem);
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
                        } else {
-                               the_beat.ticks = lrint (the_beat.ticks - rem);
-                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
+                               ticks = lrint (ticks - rem);
+                               DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
                        }
                } else {
                        /* on the subdivision, do nothing */
                }
        }
-
-       return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
-               (*i).tempo->frames_per_beat (_frame_rate);
+       return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
 }
 
 framepos_t
 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
 {
-       require_map_to (frame);
-
        Glib::Threads::RWLock::ReaderLock lm (lock);
-       BBTPointList::const_iterator fi;
-
-       if (dir > 0) {
-               fi = bbt_after_or_at (frame);
-       } else {
-               fi = bbt_before_or_at (frame);
-       }
 
-       assert (fi != _map.end());
+       double const beat_at_framepos = beat_at_frame (frame);
 
-       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")));
+       BBT_Time bbt (beats_to_bbt (beat_at_framepos));
 
        switch (type) {
        case Bar:
                if (dir < 0) {
                        /* find bar previous to 'frame' */
-
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundDownMaybe) {
-                                       return frame;
-                               }
-                               --fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               if (fi == _map.begin()) {
-                                       break;
-                               }
-                               fi--;
-                       }
-                       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;
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
 
                } else if (dir > 0) {
-
                        /* find bar following 'frame' */
-
-                       if ((*fi).is_bar() && (*fi).frame == frame) {
-                               if (dir == RoundUpMaybe) {
-                                       return frame;
-                               }
-                               ++fi;
-                       }
-
-                       while (!(*fi).is_bar()) {
-                               fi++;
-                               if (fi == _map.end()) {
-                                       --fi;
-                                       break;
-                               }
-                       }
-
-                       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;
-
+                       ++bbt.bars;
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       return frame_time (bbt);
                } else {
-
                        /* true rounding: find nearest bar */
 
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
+                       framepos_t raw_ft = frame_time (bbt);
+                       bbt.beats = 1;
+                       bbt.ticks = 0;
+                       framepos_t prev_ft = frame_time (bbt);
+                       ++bbt.bars;
+                       framepos_t next_ft = frame_time (bbt);
 
-                       while ((*prev).beat != 1) {
-                               if (prev == _map.begin()) {
-                                       break;
-                               }
-                               prev--;
-                       }
-
-                       while ((next != _map.end()) && (*next).beat != 1) {
-                               next++;
-                       }
-
-                       if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
+                       if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
+                               return next_ft;
                        } else {
-                               return (*next).frame;
+                               return prev_ft;
                        }
-
                }
 
                break;
 
        case Beat:
                if (dir < 0) {
-
-                       if (fi == _map.begin()) {
-                               return 0;
-                       }
-
-                       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, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
+                       return frame_at_beat (floor (beat_at_framepos));
                } else if (dir > 0) {
-                       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, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
-                                                                    (*fi).bar, (*fi).beat, (*fi).frame));
-                       return (*fi).frame;
+                       return frame_at_beat (ceil (beat_at_framepos));
                } else {
-                       /* find beat nearest to frame */
-                       if ((*fi).frame == frame) {
-                               return frame;
-                       }
-
-                       BBTPointList::const_iterator prev = fi;
-                       BBTPointList::const_iterator next = fi;
-
-                       /* fi is already the beat before_or_at frame, and
-                          we've just established that its not at frame, so its
-                          the beat before frame.
-                       */
-                       ++next;
-
-                       if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
-                               return (*prev).frame;
-                       } else {
-                               return (*next).frame;
-                       }
+                       return frame_at_beat (floor (beat_at_framepos + 0.5));
                }
                break;
        }
 
-       abort(); /* NOTREACHED */
        return 0;
 }
 
 void
-TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
-                   TempoMap::BBTPointList::const_iterator& end,
+TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
                    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::ReaderLock lm (lock);
+       uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
+       uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
+
+       while (cnt <= upper_beat) {
+               framecnt_t const pos = frame_at_beat (cnt);
+               MeterSection const meter = meter_section_at (pos);
+               Tempo const tempo = tempo_at (pos);
+               BBT_Time const bbt = beats_to_bbt ((double) cnt);
 
-       begin = lower_bound (_map.begin(), _map.end(), lower);
-       end = upper_bound (_map.begin(), _map.end(), upper);
+               points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
+               ++cnt;
+       }
 }
 
 const TempoSection&
 TempoMap::tempo_section_at (framepos_t frame) const
 {
        Glib::Threads::RWLock::ReaderLock lm (lock);
+
        Metrics::const_iterator i;
        TempoSection* prev = 0;
 
@@ -1614,11 +1741,34 @@ TempoMap::tempo_section_at (framepos_t frame) const
        return *prev;
 }
 
-const Tempo&
+const Tempo
 TempoMap::tempo_at (framepos_t frame) const
 {
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
        TempoMetric m (metric_at (frame));
+       TempoSection* prev_ts = 0;
+
+       Metrics::const_iterator i;
+
+       for (i = metrics.begin(); i != metrics.end(); ++i) {
+               TempoSection* t;
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if ((prev_ts) && t->frame() > frame) {
+                               /* this is the one past frame */
+                               framepos_t const time = frame - prev_ts->frame();
+                               framepos_t const last_time = t->frame() - prev_ts->frame();
+                               double const last_beats_per_minute = t->beats_per_minute();
+                               double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+                               Tempo const ret_tempo (ret, m.tempo().note_type ());
+                               return ret_tempo;
+                       }
+                       prev_ts = t;
+               }
+       }
+
        return m.tempo();
+
 }
 
 const MeterSection&
@@ -1856,8 +2006,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
                                // which is correct for our purpose
                        }
 
-                       BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
-                       bbt_time ((*i)->frame(), bbt, bi);
+                       bbt_time ((*i)->frame(), bbt);
 
                        // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
 
@@ -1970,205 +2119,14 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
 framepos_t
 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_iterator next_tempo;
-       const TempoSection* tempo = 0;
-
-       /* Find the starting tempo metric */
-
-       for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
-
-               const TempoSection* t;
-
-               if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
-
-                       /* This is a bit of a hack, but pos could be -ve, and if it is,
-                          we consider the initial metric changes (at time 0) to actually
-                          be in effect at pos.
-                       */
-
-                       framepos_t f = (*next_tempo)->frame ();
-
-                       if (pos < 0 && f == 0) {
-                               f = pos;
-                       }
-
-                       if (f > pos) {
-                               break;
-                       }
-
-                       tempo = t;
-               }
-       }
-
-       /* We now have:
-
-          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()));
-
-       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::Beats distance_beats = Evoral::Beats::ticks_at_rate(
-                       distance_frames, tempo->frames_per_beat (_frame_rate));
-
-               /* Amount to subtract this time */
-               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()),
-                                                              distance_frames, distance_beats));
-
-               /* Update */
-               beats -= delta;
-               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));
-
-               /* step forwards to next tempo section */
-
-               if (next_tempo != metrics.end()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*next_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                                      *((const Tempo*)tempo), tempo->frame(),
-                                                                      tempo->frames_per_beat (_frame_rate)));
-
-                       while (next_tempo != metrics.end ()) {
-
-                               ++next_tempo;
-
-                               if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return pos;
+       return frame_at_beat (beat_at_frame (pos) + beats.to_double());
 }
 
 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
 framepos_t
 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_reverse_iterator prev_tempo;
-       const TempoSection* tempo = 0;
-
-       /* Find the starting tempo metric */
-
-       for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
-
-               const TempoSection* t;
-
-               if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
-
-                       /* This is a bit of a hack, but pos could be -ve, and if it is,
-                          we consider the initial metric changes (at time 0) to actually
-                          be in effect at pos.
-                       */
-
-                       framepos_t f = (*prev_tempo)->frame ();
-
-                       if (pos < 0 && f == 0) {
-                               f = pos;
-                       }
-
-                       /* this is slightly more complex than the forward case
-                          because we reach the tempo in effect at pos after
-                          passing through pos (rather before, as in the
-                          forward case). having done that, we then need to
-                          keep going to get the previous tempo (or
-                          metrics.rend())
-                       */
-
-                       if (f <= pos) {
-                               if (tempo == 0) {
-                                       /* first tempo with position at or
-                                          before pos
-                                       */
-                                       tempo = t;
-                               } else if (f < pos) {
-                                       /* some other tempo section that
-                                          is even earlier than 'tempo'
-                                       */
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       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(),
-                                    prev_tempo == metrics.rend()));
-
-       /* We now have:
-
-          tempo       -> the Tempo for "pos"
-          prev_tempo  -> the first metric before "pos", possibly metrics.rend()
-       */
-
-       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::Beats distance_beats = Evoral::Beats::ticks_at_rate(
-                       distance_frames, tempo->frames_per_beat (_frame_rate));
-
-               /* Amount to subtract this time */
-               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.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())));
-
-               /* step backwards to prior TempoSection */
-
-               if (prev_tempo != metrics.rend()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath,
-                                    string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                    *((const Tempo*)tempo), tempo->frame(),
-                                                    tempo->frames_per_beat (_frame_rate)));
-
-                       while (prev_tempo != metrics.rend ()) {
-
-                               ++prev_tempo;
-
-                               if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
-                                       break;
-                               }
-                       }
-               } else {
-                       pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
-                       beats = Evoral::Beats();
-               }
-       }
-
-       return pos;
+       return frame_at_beat (beat_at_frame (pos) - beats.to_double());
 }
 
 /** Add the BBT interval op to pos and return the result */
@@ -2305,6 +2263,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
 
        return pos;
+
 }
 
 /** Count the number of beats that are equivalent to distance when going forward,
@@ -2313,126 +2272,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
-       Glib::Threads::RWLock::ReaderLock lm (lock);
-       Metrics::const_iterator next_tempo;
-       const TempoSection* tempo = 0;
-       framepos_t effective_pos = max (pos, (framepos_t) 0);
-
-       /* Find the relevant initial tempo metric  */
-
-       for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
-
-               const TempoSection* t;
-
-               if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
-
-                       if ((*next_tempo)->frame() > effective_pos) {
-                               break;
-                       }
-
-                       tempo = t;
-               }
-       }
-
-       /* We now have:
-
-          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()));
-
-       Evoral::Beats beats = Evoral::Beats();
-
-       while (distance) {
-
-               /* End of this section */
-               framepos_t end;
-               /* Distance to `end' in frames */
-               framepos_t distance_to_end;
-
-               if (next_tempo == metrics.end ()) {
-                       /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
-                       end = max_framepos;
-                       distance_to_end = max_framepos;
-               } else {
-                       end = (*next_tempo)->frame ();
-                       distance_to_end = end - pos;
-               }
-
-               /* 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));
-
-               /* Update */
-               pos += sub;
-               distance -= sub;
-               assert (tempo);
-               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));
-
-               /* Move on if there's anything to move to */
-
-               if (next_tempo != metrics.end()) {
-
-                       tempo = dynamic_cast<const TempoSection*>(*next_tempo);
-
-                       DEBUG_TRACE (DEBUG::TempoMath,
-                                    string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
-                                                    *((const Tempo*)tempo), tempo->frame(),
-                                                    tempo->frames_per_beat (_frame_rate)));
-
-                       while (next_tempo != metrics.end ()) {
-
-                               ++next_tempo;
-
-                               if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
-                                       break;
-                               }
-                       }
-
-                       if (next_tempo == metrics.end()) {
-                               DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
-                       } else {
-                               DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
-                                                                              **next_tempo, (*next_tempo)->frame()));
-                       }
-
-               }
-               assert (tempo);
-       }
-
-       return beats;
-}
-
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_before_or_at (framepos_t pos)
-{
-       /* CALLER MUST HOLD READ LOCK */
-
-       BBTPointList::const_iterator i;
-
-       if (pos < 0) {
-               /* not really correct, but we should catch pos < 0 at a higher
-                  level
-               */
-               return _map.begin();
-       }
-
-       i = lower_bound (_map.begin(), _map.end(), pos);
-       assert (i != _map.end());
-       if ((*i).frame > pos) {
-               assert (i != _map.begin());
-               --i;
-       }
-       return i;
+       return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
 }
 
 struct bbtcmp {
@@ -2441,40 +2281,6 @@ struct bbtcmp {
     }
 };
 
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_before_or_at (const BBT_Time& bbt)
-{
-       BBTPointList::const_iterator i;
-       bbtcmp cmp;
-
-       i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
-       assert (i != _map.end());
-       if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
-               assert (i != _map.begin());
-               --i;
-       }
-       return i;
-}
-
-TempoMap::BBTPointList::const_iterator
-TempoMap::bbt_after_or_at (framepos_t pos)
-{
-       /* CALLER MUST HOLD READ LOCK */
-
-       BBTPointList::const_iterator i;
-
-       if (_map.back().frame == pos) {
-               i = _map.end();
-               assert (i != _map.begin());
-               --i;
-               return i;
-       }
-
-       i = upper_bound (_map.begin(), _map.end(), pos);
-       assert (i != _map.end());
-       return i;
-}
-
 std::ostream&
 operator<< (std::ostream& o, const Meter& m) {
        return o << m.divisions_per_bar() << '/' << m.note_divisor();
index 1e9d9568761a88b4a0a14f6a740d22d4f290345e..430ee5b0e7d1e56e89fb996b26971d0fd0043919 100644 (file)
@@ -121,7 +121,7 @@ JACKSession::timebase_callback (jack_transport_state_t /*state*/,
        TempoMetric metric (tempo_map.metric_at (tf));
 
        try {
-               tempo_map.bbt_time_rt (tf, bbt);
+               tempo_map.bbt_time (tf, bbt);
 
                pos->bar = bbt.bars;
                pos->beat = bbt.beats;
index e3f23fb10c7d4643c4a6312c7add479fc6015ec1..74755f6eccced0de6772ee26109b4d24f86a3400 100644 (file)
@@ -165,10 +165,10 @@ Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const
                         }
                        break;
                case Mark::Minor:
-                       cr->rel_line_to (0, -height/4.0);
+                       cr->rel_line_to (0, -height/3.0);
                        break;
                case Mark::Micro:
-                       cr->rel_line_to (0, -height/16.0);
+                       cr->rel_line_to (0, -height/5.0);
                        break;
                }
                cr->stroke ();