+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
+}
+
+void
+TempoSection::set_type (Type type)
+{
+ _type = type;
+}
+
+/** returns the tempo at the zero-based (relative to session) frame.
+*/
+double
+TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
+{
+
+ if (_type == Constant) {
+ return beats_per_minute();
+ }
+
+ return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based frame (relative to session)
+ where the tempo occurs in this section.
+ beat b is only used for constant tempos.
+ note that the tempo map may have multiple such values.
+*/
+framepos_t
+TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
+{
+ if (_type == Constant) {
+ return ((b - beat()) * frames_per_beat (frame_rate)) + frame();
+ }
+
+ return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate) + frame();
+}
+/** returns the tempo at the zero-based (relative to session) beat.
+*/
+double
+TempoSection::tempo_at_beat (double b) const
+{
+
+ if (_type == Constant) {
+ return beats_per_minute();
+ }
+
+ return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based beat (relative to session)
+ where the tempo occurs given frame f. frame f is only used for constant tempos.
+ note that the session tempo map may have multiple beats at a given tempo.
+*/
+double
+TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
+{
+ if (_type == Constant) {
+ double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
+ return ticks / BBT_Time::ticks_per_beat;
+ }
+
+ return (tick_at_tick_tempo (bpm * BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based beat (relative to session origin)
+ where the zero-based frame (relative to session)
+ lies.
+*/
+double
+TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
+{
+ return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
+}
+
+/** returns the zero-based frame (relative to session start frame)
+ where the zero-based beat (relative to session start)
+ falls.
+*/
+
+framepos_t
+TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
+{
+ return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
+}
+
+/** returns the zero-based tick (relative to session origin)
+ where the zero-based frame (relative to tempo section)
+ lies.
+*/
+double
+TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
+{
+ if (_type == Constant) {
+ return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
+ }
+
+ return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
+}
+
+/** returns the zero-based frame (relative to session origin)
+ where the zero-based tick (relative to session)
+ falls.
+*/
+framepos_t
+TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
+{
+ if (_type == Constant) {
+ return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
+ }
+
+ return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
+}
+
+/*
+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))
+
+with function constant
+c = log(Ta/T0)/a
+so
+a = log(Ta/T0)/c
+
+The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
+b(t) = T0(e^(ct) - 1) / c
+
+To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
+t(b) = log((cb / T0) + 1) / c
+
+The time t at which Tempo T occurs is a as above:
+t(T) = log(T / T0) / c
+
+The beat at which a Tempo T occurs is:
+b(T) = (T - T0) / c
+
+The Tempo at which beat b occurs is:
+T(b) = b.c + T0
+
+We define c for this tempo ramp by placing a new tempo section at some time t after this one.
+Our problem is that we usually don't know t.
+We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
+Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
+t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
+
+By substituting our expanded t as a in the c function above, our problem is reduced to:
+c = T0 (e^(log (Ta / T0)) - 1) / b
+
+We can now store c for future time calculations.
+If the following tempo section (the one that defines c in conjunction with this one)
+is changed or moved, c is no longer valid.
+
+The public methods are session-relative.
+
+Most of this stuff is taken from this paper:
+
+WHERE’S THE BEAT?
+TOOLS FOR DYNAMIC TEMPO CALCULATIONS
+Jan C. Schacher
+Martin Neukom
+Zurich University of Arts
+Institute for Computer Music and Sound Technology
+
+https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
+
+*/
+
+/* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
+void
+TempoSection::set_c_func_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());
+ _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
+}
+
+/* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
+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(), frame_rate));
+}
+
+framecnt_t
+TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
+{
+ return (framecnt_t) floor ((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;
+}
+
+/* position function */
+double
+TempoSection::a_func (double end_tpm, double c_func) const
+{
+ return log (end_tpm / ticks_per_minute()) / c_func;
+}
+
+/*function constant*/
+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) const
+{
+ return exp (_c_func * time) * ticks_per_minute();
+}
+
+/* time in minutes at tempo in tpm */
+double
+TempoSection::time_at_tick_tempo (double tick_tempo) const
+{
+ return log (tick_tempo / ticks_per_minute()) / _c_func;
+}
+
+/* tick at tempo in tpm */
+double
+TempoSection::tick_at_tick_tempo (double tick_tempo) const
+{
+ return (tick_tempo - ticks_per_minute()) / _c_func;
+}
+
+/* tempo in tpm at tick */
+double
+TempoSection::tick_tempo_at_tick (double tick) const
+{
+ return (tick * _c_func) + ticks_per_minute();
+}
+
+/* tick at time in minutes */
+double
+TempoSection::tick_at_time (double time) const
+{
+ return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
+}
+
+/* time in minutes at tick */
+double
+TempoSection::time_at_tick (double tick) const
+{
+ return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
+}
+
+/* beat at time in minutes */
+double
+TempoSection::beat_at_time (double time) const
+{
+ return tick_at_time (time) / BBT_Time::ticks_per_beat;
+}
+
+/* time in munutes at beat */
+double
+TempoSection::time_at_beat (double beat) const
+{
+ return time_at_tick (beat * BBT_Time::ticks_per_beat);