Tempo ramps - Remove the tick walk, store c_func, document the approach.
authornick_m <mainsbridge@gmail.com>
Thu, 25 Feb 2016 16:10:15 +0000 (03:10 +1100)
committernick_m <mainsbridge@gmail.com>
Fri, 27 May 2016 13:38:10 +0000 (23:38 +1000)
gtk2_ardour/editor_drag.cc
libs/ardour/ardour/tempo.h
libs/ardour/tempo.cc

index 4bde8e908c35aeae89eeb11972103cfa6b14b1d0..45a6245bc7b019a69a5ebb1e9399de8a380f43f9 100644 (file)
@@ -3343,14 +3343,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(), map.beat_at_frame (_marker->position()), _marker->tempo().type());
+               map.add_tempo (_marker->tempo(), _real_section->beat(), _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.replace_tempo (*_real_section, _marker->tempo().beats_per_minute() , map.beat_at_frame (_marker->position()), _marker->tempo().type());
+               map.replace_tempo (*_real_section, _marker->tempo().beats_per_minute() , _real_section->beat(), _marker->tempo().type());
                XMLNode &after = map.get_state();
                _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
                _editor->commit_reversible_command ();
index bc52c489024bab747964fb5538a418e6bcdbbdb3..d0c8381b2b711cb5d135b5201bcdc2666bc781a0 100644 (file)
@@ -165,9 +165,9 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        };
 
        TempoSection (const double& beat, double qpm, double note_type, Type tempo_type)
-               : MetricSection (beat), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type)  {}
+               : MetricSection (beat), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type), _c_func (0.0)  {}
        TempoSection (framepos_t frame, double qpm, double note_type, Type tempo_type)
-               : MetricSection (frame), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {}
+               : MetricSection (frame), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type), _c_func (0.0) {}
        TempoSection (const XMLNode&);
 
        static const std::string xml_state_node_name;
@@ -181,14 +181,20 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        void set_type (Type type);
        Type 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 tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_tempo (double tempo, 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 tick_at_frame (framepos_t frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_tick (double tick, 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;
+       double beat_at_frame (framepos_t frame, framecnt_t frame_rate) const;
+       framepos_t frame_at_beat (double beat, framecnt_t frame_rate) const;
+
+       framecnt_t ramp_duration_from_tempo_and_beat (double end_tpm, double end_beat, framecnt_t frame_rate);
+
+       double compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+       double get_c_func () const { return _c_func; }
+       void set_c_func (double c_func) { _c_func = c_func; }
 
        Timecode::BBT_Time legacy_bbt () { return _legacy_bbt; }
 
@@ -201,17 +207,17 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
         * '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 end_tpm, double c_func) const;
+       double c_func (double end_tpm, 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_tempo_at_time (double time) const;
+       double time_at_tick_tempo (double tick_tempo) 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 tick_at_time (double time) const;
+       double time_at_tick (double tick) 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;
+       double beat_at_time (double time) const;
+       double time_at_beat (double beat) 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
@@ -223,6 +229,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        */
        double _bar_offset;
        Type _type;
+       double _c_func;
        Timecode::BBT_Time _legacy_bbt;
 };
 
index 08a70c4574f9a3f699f63d742047c07aba15fe55..dc19d8141e38f1966d79482a614db70bf4273b76 100644 (file)
@@ -106,7 +106,6 @@ TempoSection::TempoSection (const XMLNode& node)
                error << _("TempoSection XML node has no \"beat\" property") << endmsg;
        }
 
-
        if ((prop = node.property ("beats-per-minute")) == 0) {
                error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
                throw failed_constructor();
@@ -191,27 +190,27 @@ TempoSection::set_type (Type type)
 /** returns the tempo at the zero-based (relative to tempo section) frame.
 */
 double
-TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::tempo_at_frame (framepos_t 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;
+       return tick_tempo_at_time (frame_to_minute (frame, frame_rate)) / BBT_Time::ticks_per_beat;
 }
 
 /** returns the zero-based frame (relative to tempo section)
    where the tempo occurs.
 */
 framepos_t
-TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::frame_at_tempo (double bpm, 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);
+       return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate);
 }
 
 /** returns the zero-based tick (relative to tempo section)
@@ -219,13 +218,13 @@ TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame
    lies.
 */
 double
-TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::tick_at_frame (framepos_t frame, framecnt_t frame_rate) const
 {
        if (_type == Constant) {
                return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
        }
 
-       return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
+       return tick_at_time (frame_to_minute (frame, frame_rate));
 }
 
 /** returns the zero-based frame (relative to tempo section)
@@ -233,13 +232,13 @@ TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_fr
    falls.
 */
 framepos_t
-TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::frame_at_tick (double tick, framecnt_t frame_rate) const
 {
        if (_type == Constant) {
                return (framepos_t) floor ((tick  / BBT_Time::ticks_per_beat) * 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);
+       return minute_to_frame (time_at_tick (tick), frame_rate);
 }
 
 /** returns the zero-based beat (relative to tempo section)
@@ -247,9 +246,9 @@ TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame,
    lies.
 */
 double
-TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
 {
-       return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
+       return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
 }
 
 /** returns the zero-based frame (relative to tempo section start frame)
@@ -258,15 +257,80 @@ TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_fr
 */
 
 framepos_t
-TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
 {
-       return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
+       return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
+}
+/*
+Ramp Overview
+
+
+Tempo |                   *
+Tt----|-----------------*|
+Ta----|--------------|*  |
+      |            * |   |
+      |         *    |   |
+      |     *        |   |
+T0----|*             |   |
+      |              |   |
+      _______________|___|_____
+      time           a   t (next tempo)
+      [        c         ] defines c
+
+Duration in beats at time a is the integral of some Tempo function.
+In our case, the Tempo function (Tempo at time t) is
+T(t) = T0(e^(ct))
+
+where c is the function constant:
+c = log(Ta/T0)/a
+and
+a = log(Ta/T0)/c
+
+We define c for a tempo ramp by placing a new tempo at some distance t away from our existing one.
+
+Given the function constant, the beat function (duration in beats at some time t) is:
+b(t) = T0(e^(ct) - 1) / c
+
+To find the time t at any beat b on the curve, we use the inverse function of the beat function:
+t(b) = log((cb / T0) + 1) / c
+
+When we add or move the next tempo section, we change the function constant. We take advantage of t being equal to a here.
+The problem is that we usually don't know t.
+We do know the beat duration until the next tempo section.
+Substituting t = a into the beat function allows us to find a in terms of beat duration b and the two relevant tempos:
+a = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
+
+We then use a to set the function constant c (above).
+
+Most of this stuff is taken from this paper:
+WHERE’S THE BEAT?
+TOOLS FOR DYNAMIC TEMPO CALCULATIONS
+Jan C. Schacher
+Martin Neukom
+
+https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
+
+*/
+
+/* compute the duration of this tempo section givan the end tempo and duration in beats of some later tempo section*/
+framecnt_t
+TempoSection::ramp_duration_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
+{
+       double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
+       return minute_to_frame (((end_beat * BBT_Time::ticks_per_beat) * log_tempo_ratio) / (ticks_per_minute() * (exp (log_tempo_ratio) - 1)), frame_rate);
+}
+
+/* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from this tempo section */
+double
+TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+{
+       return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
 }
 
 framecnt_t
 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
 {
-       return time * 60.0 * frame_rate;
+       return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
 }
 
 double
@@ -291,44 +355,44 @@ TempoSection::c_func (double end_tpm, double end_time) const
 
 /* tempo in tpm at time in minutes */
 double
-TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
+TempoSection::tick_tempo_at_time (double time) const
 {
-       return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
+       return exp (_c_func * 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
+TempoSection::time_at_tick_tempo (double tick_tempo) const
 {
-       return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
+       return log (tick_tempo / ticks_per_minute()) / _c_func;
 }
 
 /* tick at time in minutes */
 double
-TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
+TempoSection::tick_at_time (double time) const
 {
-       return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
+       return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
 }
 
 /* time in minutes at tick */
 double
-TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
+TempoSection::time_at_tick (double tick) const
 {
-       return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
+       return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
 }
 
 /* beat at time in minutes */
 double
-TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
+TempoSection::beat_at_time (double time) const
 {
-       return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
+       return tick_at_time (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
+TempoSection::time_at_beat (double beat) const
 {
-       return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
+       return time_at_tick (beat * BBT_Time::ticks_per_beat);
 }
 
 void
@@ -707,7 +771,6 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const doubl
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                TempoSection& first (first_tempo());
-
                if (ts.beat() != first.beat()) {
                        remove_tempo_locked (ts);
                        add_tempo_locked (tempo, where, true, type);
@@ -740,7 +803,6 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_
                } else {
                        /*AudioTime*/
                        ts.set_frame (frame);
-
                        MetricSectionFrameSorter fcmp;
                        metrics.sort (fcmp);
 
@@ -772,8 +834,10 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_
                        }
 
                        if (prev_ts) {
-                               /* set the start beat */
-                               double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
+                               /* set the start beat - we need to reset the function constant before beat calculations make sense*/
+                               prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate));
+
+                               double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), _frame_rate);
                                double beats = beats_to_ts + prev_ts->beat();
 
                                if (next_ts) {
@@ -791,6 +855,8 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_
                                        }
                                } else {
                                        ts.set_beat (beats);
+                                       ts.set_c_func (0.0);
+
                                }
                                MetricSectionSorter cmp;
                                metrics.sort (cmp);
@@ -1058,31 +1124,17 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
 
                        if (prev_ts) {
-                               double const beats_relative_to_prev_ts = t->beat() - prev_ts->beat();
-                               double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
+                               double const ticks_relative_to_prev = (t->beat() - prev_ts->beat()) * BBT_Time::ticks_per_beat;
 
-                               /* assume (falsely) that the target tempo is constant */
-                               double const t_fpb = t->frames_per_beat (_frame_rate);
-                               double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
-                               /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
-                               double length_estimate = beats_relative_to_prev_ts * av_fpb;
-
-                               if (prev_ts->type() == TempoSection::Constant) {
-                                       length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
-                               }
-
-                               double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
-                               double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
-
-                               while (fabs (tick_error) > system_precision_at_target_tempo) {
-
-                                       double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
-                                                                                           (framepos_t) length_estimate, _frame_rate);
-                                       tick_error = ticks_relative_to_prev_ts - actual_ticks;
-                                       length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
+                               framecnt_t duration;
+                               if (prev_ts->type() == TempoSection::Ramp) {
+                                       duration = prev_ts->ramp_duration_from_tempo_and_beat (t->beats_per_minute(), t->beat() - prev_ts->beat(), _frame_rate);
+                               } else {
+                                       duration = (framecnt_t) floor (ticks_relative_to_prev * prev_ts->frames_per_beat (_frame_rate) * BBT_Time::ticks_per_beat);
                                }
 
-                               t->set_frame (length_estimate + prev_ts->frame());
+                               prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), duration, _frame_rate));
+                               t->set_frame (duration + prev_ts->frame());
                        }
                        prev_ts = t;
                }
@@ -1298,10 +1350,8 @@ TempoMap::tick_at_frame (framecnt_t frame) const
                                /*the previous ts is the one containing the frame */
 
                                framepos_t const time = frame - prev_ts->frame();
-                               framepos_t const last_frame = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
 
-                               return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
+                               return prev_ts->tick_at_frame (time, _frame_rate) + accumulated_ticks;
                        }
 
                        if (prev_ts && t->frame() > prev_ts->frame()) {
@@ -1343,10 +1393,8 @@ TempoMap::frame_at_tick (double tick) const
                                /* prev_ts is the one affecting us. */
 
                                double const ticks_in_section = tick - accumulated_ticks_to_prev;
-                               framepos_t const last_time = t->frame() - prev_ts->frame();
-                               double const last_beats_per_minute = t->beats_per_minute();
 
-                               return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
+                               return prev_ts->frame_at_tick (ticks_in_section, _frame_rate) + prev_ts->frame();
                        }
                        accumulated_ticks_to_prev = accumulated_ticks;
                        prev_ts = t;
@@ -1420,14 +1468,10 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
                }
        }
        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 tick_at_time = first->tick_at_frame (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);
+               double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
 
                return time_at_bbt - time;
        }
@@ -1670,7 +1714,7 @@ TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
        }
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), _frame_rate));
        }
        /* must be treated as constant tempo */
        return ts_at->frames_per_beat (_frame_rate);
@@ -1692,9 +1736,7 @@ TempoMap::tempo_at (framepos_t frame) const
                        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);
+                               double const ret = prev_ts->tempo_at_frame (time, _frame_rate);
                                Tempo const ret_tempo (ret, m.tempo().note_type ());
                                return ret_tempo;
                        }
@@ -2191,7 +2233,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                }
 
                                if (next_tempo) {
-                                       pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+                                       pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
                                } else {
                                        pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
                                }
@@ -2211,7 +2253,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
 
        if (next_tempo) {
-               pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+               pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
        } else {
                pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
        }
@@ -2243,7 +2285,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
                                }
 
                                if (next_tempo) {
-                                       pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+                                       pos += tempo->frame_at_beat (beats, _frame_rate);
                                } else {
                                        pos += llrint (beats * frames_per_beat);
                                }
@@ -2262,13 +2304,13 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
        }
 
        if (next_tempo) {
-               pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+               pos += tempo->frame_at_beat (beats, _frame_rate);
        } else {
                pos += llrint (beats * frames_per_beat);
        }
 
        if (op.ticks) {
-               pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
+               pos += tempo->frame_at_tick (op.ticks, _frame_rate);
        }
 
        return pos;