move latency-recompute into dedicated thread.
[ardour.git] / libs / ardour / tempo.cc
index 693f5234ee14464c5a5bf9bac10ac49273b24df9..c5c7e2c52fa90ad158c22b4126f4c0473acade76 100644 (file)
@@ -1860,6 +1860,18 @@ TempoMap::bbt_at_frame (framepos_t frame)
        return bbt_at_frame_locked (_metrics, frame);
 }
 
+BBT_Time
+TempoMap::bbt_at_frame_rt (framepos_t frame)
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
+
+       if (!lm.locked()) {
+               throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
+       }
+
+       return bbt_at_frame_locked (_metrics, frame);
+}
+
 Timecode::BBT_Time
 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
 {
@@ -2015,6 +2027,9 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                        if (prev_t) {
                                if (t == section) {
                                        section_prev = prev_t;
+                                       if (t->locked_to_meter()) {
+                                               prev_t = t;
+                                       }
                                        continue;
                                }
                                if (t->position_lock_style() == MusicTime) {
@@ -2038,11 +2053,15 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
                }
        }
 
+#if (0)
        recompute_tempos (imaginary);
 
        if (check_solved (imaginary)) {
                return true;
+       } else {
+               dunp (imaginary, std::cout);
        }
+#endif
 
        MetricSectionFrameSorter fcmp;
        imaginary.sort (fcmp);
@@ -2099,11 +2118,15 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub
                section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
        }
 
+#if (0)
        recompute_tempos (imaginary);
 
        if (check_solved (imaginary)) {
                return true;
+       } else {
+               dunp (imaginary, std::cout);
        }
+#endif
 
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
@@ -2505,15 +2528,42 @@ TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
 }
 
 void
-TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame)
+TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
 {
        Metrics future_map;
+       bool was_musical = ts->position_lock_style() == MusicTime;
+
+       if (sub_num == 0 && was_musical) {
+               /* if we're not snapping to music,
+                  AudioTime and MusicTime may be treated identically.
+               */
+               ts->set_position_lock_style (AudioTime);
+       }
 
        if (ts->position_lock_style() == MusicTime) {
                {
+                       /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
                        Glib::Threads::RWLock::WriterLock lm (lock);
                        TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
-                       const double pulse = pulse_at_frame_locked (future_map, frame);
+                       double beat = beat_at_frame_locked (future_map, frame);
+
+                       if (sub_num > 1) {
+                               beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
+                       } else if (sub_num == 1) {
+                               /* snap to beat */
+                               beat = floor (beat + 0.5);
+                       }
+
+                       double pulse = pulse_at_beat_locked (future_map, beat);
+
+                       if (sub_num == -1) {
+                               /* snap to  bar */
+                               BBT_Time bbt = bbt_at_beat_locked (future_map, beat);
+                               bbt.beats = 1;
+                               bbt.ticks = 0;
+                               pulse = pulse_at_bbt_locked (future_map, bbt);
+                       }
+
                        if (solve_map_pulse (future_map, tempo_copy, pulse)) {
                                solve_map_pulse (_metrics, ts, pulse);
                                recompute_meters (_metrics);
@@ -2532,6 +2582,10 @@ TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame)
                }
        }
 
+       if (sub_num == 0 && was_musical) {
+               ts->set_position_lock_style (MusicTime);
+       }
+
        Metrics::const_iterator d = future_map.begin();
        while (d != future_map.end()) {
                delete (*d);
@@ -2648,33 +2702,40 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                                }
                        }
                }
+               /* minimum allowed measurement distance in frames */
+               const framepos_t min_dframe = 2;
 
                /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
                   constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
                */
                double contribution = 0.0;
-               double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
 
                if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
                        contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
                }
 
-               frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
-               double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+               const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
+
+               const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
+               const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+
                double new_bpm;
 
                if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
 
                        if (prev_t->position_lock_style() == MusicTime) {
                                if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+                                       if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
 
-                                       new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
-                                                                               / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
-
+                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
+                                                                                       / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
+                                       } else {
+                                               new_bpm = prev_t->beats_per_minute();
+                                       }
                                } else {
                                        /* prev to prev is irrelevant */
 
-                                       if (start_pulse != prev_t->pulse()) {
+                                       if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
                                                new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
                                        } else {
                                                new_bpm = prev_t->beats_per_minute();
@@ -2683,12 +2744,17 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                        } else {
                                /* AudioTime */
                                if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
-                                       new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
-                                                                               / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
+                                       if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
+
+                                               new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
+                                                                                       / (double) ((end_frame) - prev_to_prev_t->frame()));
+                                       } else {
+                                               new_bpm = prev_t->beats_per_minute();
+                                       }
                                } else {
                                        /* prev_to_prev_t is irrelevant */
 
-                                       if (end_frame != prev_t->frame()) {
+                                       if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
                                                new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
                                        } else {
                                                new_bpm = prev_t->beats_per_minute();
@@ -2697,22 +2763,34 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra
                        }
                } else {
 
-                       double frame_ratio;
-                       double pulse_ratio;
+                       double frame_ratio = 1.0;
+                       double pulse_ratio = 1.0;
                        const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
 
                        if (prev_to_prev_t) {
-
-                               frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
-                               pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
+                               if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
+                                       frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
+                               }
+                               if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
+                                       pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
+                               }
                        } else {
-
-                               frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
+                               if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
+                                       frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
+                               }
                                pulse_ratio = (start_pulse / end_pulse);
                        }
                        new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
                }
 
+               /* don't clamp and proceed here.
+                  testing has revealed that this can go negative,
+                  which is an entirely different thing to just being too low.
+               */
+               if (new_bpm < 0.5) {
+                       return;
+               }
+               new_bpm = min (new_bpm, (double) 1000.0);
                prev_t->set_beats_per_minute (new_bpm);
                recompute_tempos (future_map);
                recompute_meters (future_map);
@@ -2918,6 +2996,11 @@ TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
        if (cnt < 0.0) {
                cnt = 0.0;
        }
+
+       if (frame_at_beat_locked (_metrics, cnt) >= upper) {
+               return;
+       }
+
        while (pos < upper) {
                pos = frame_at_beat_locked (_metrics, cnt);
                const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
@@ -3010,7 +3093,7 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con
        }
 
        if (ts_after) {
-               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
+               return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
        }
        /* must be treated as constant tempo */
        return ts_at->frames_per_beat (_frame_rate);
@@ -3508,14 +3591,18 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount)
 framepos_t
 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
 {
-       return frame_at_beat (beat_at_frame (pos) + beats.to_double());
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, 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
 {
-       return frame_at_beat (beat_at_frame (pos) - beats.to_double());
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
 }
 
 /** Add the BBT interval op to pos and return the result */
@@ -3549,7 +3636,9 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
 Evoral::Beats
 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
 {
-       return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+
+       return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
 }
 
 struct bbtcmp {