+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, pulse(), m.divisions_per_bar()));
+}
+
+void
+TempoSection::set_type (Type type)
+{
+ _type = type;
+}
+
+/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
+*/
+double
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+{
+
+ if (_type == Constant) {
+ return pulses_per_minute();
+ }
+
+ return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
+}
+
+/** returns the zero-based frame (relative to session)
+ where the tempo in whole pulses per minute 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 (const double& ppm, const double& b, const framecnt_t& frame_rate) const
+{
+ if (_type == Constant) {
+ return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
+ }
+
+ return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
+}
+/** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
+*/
+double
+TempoSection::tempo_at_pulse (const double& p) const
+{
+
+ if (_type == Constant) {
+ return pulses_per_minute();
+ }
+ double const ppm = pulse_tempo_at_pulse (p - pulse());
+ return ppm;
+}
+
+/** returns the zero-based beat (relative to session)
+ where the tempo in whole pulses per minute 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::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
+{
+ if (_type == Constant) {
+ double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+ return beats;
+ }
+
+ return pulse_at_pulse_tempo (ppm) + pulse();
+}
+
+/** returns the zero-based pulse (relative to session origin)
+ where the zero-based frame (relative to session)
+ lies.
+*/
+double
+TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+{
+ if (_type == Constant) {
+ return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
+ }
+
+ return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
+}
+
+/** returns the zero-based frame (relative to session start frame)
+ where the zero-based pulse (relative to session start)
+ falls.
+*/
+
+framepos_t
+TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
+{
+ if (_type == Constant) {
+ return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+ }
+
+ return minute_to_frame (time_at_pulse (p - pulse()), 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
+
+*/
+
+/*
+ compute this ramp's function constant using the end tempo (in whole pulses per minute)
+ and duration (pulses into global start) of some later tempo section.
+*/
+double
+TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
+{
+ double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
+ return pulses_per_minute() * (exp (log_tempo_ratio) - 1) / (end_pulse - pulse());
+}
+
+/* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
+double
+TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
+{
+ return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
+}
+
+framecnt_t
+TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
+{
+ return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
+}
+
+double
+TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
+{
+ return (frame / (double) frame_rate) / 60.0;
+}
+
+/* position function */
+double
+TempoSection::a_func (double end_bpm, double c_func) const
+{
+ return log (end_bpm / pulses_per_minute()) / c_func;
+}
+
+/*function constant*/
+double
+TempoSection::c_func (double end_bpm, double end_time) const
+{
+ return log (end_bpm / pulses_per_minute()) / end_time;
+}
+
+/* tempo in ppm at time in minutes */
+double
+TempoSection::pulse_tempo_at_time (const double& time) const
+{
+ return exp (_c_func * time) * pulses_per_minute();
+}
+
+/* time in minutes at tempo in ppm */
+double
+TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
+{
+ return log (pulse_tempo / pulses_per_minute()) / _c_func;
+}
+
+/* tick at tempo in ppm */
+double
+TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
+{
+ return (pulse_tempo - pulses_per_minute()) / _c_func;
+}
+
+/* tempo in ppm at tick */
+double
+TempoSection::pulse_tempo_at_pulse (const double& pulse) const
+{
+ return (pulse * _c_func) + pulses_per_minute();
+}
+
+/* pulse at time in minutes */
+double
+TempoSection::pulse_at_time (const double& time) const
+{
+ return ((exp (_c_func * time)) - 1) * (pulses_per_minute() / _c_func);
+}
+
+/* time in minutes at pulse */
+double
+TempoSection::time_at_pulse (const double& pulse) const
+{
+ return log (((_c_func * pulse) / pulses_per_minute()) + 1) / _c_func;