fix bypassing plugins with sidechain i/o
[ardour.git] / libs / ardour / tempo.cc
index 646a15b6f345ba42f0253961f58ad47b234eb18b..c5c7e2c52fa90ad158c22b4126f4c0473acade76 100644 (file)
@@ -589,17 +589,20 @@ MeterSection::get_state() const
   Tempo is the rate of the musical pulse.
   Meters divide the pulses into measures and beats.
 
+  TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
+  and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
   Note that Tempo::beats_per_minute() has nothing to do with musical beats.
-  It should rather be thought of as tempo note divisions per minute.
 
-  TempoSections, which are nice to think of in whole pulses per minute,
-  and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
-  and beats (via note_divisor) are used to form a tempo map.
+  MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
+
+  Both tempos and meters have a pulse position and a frame position.
+  Meters also have a beat position, which is always 0.0 for the first meter.
   TempoSections and MeterSections may be locked to either audio or music (position lock style).
-  We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
-  We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
+  The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
 
-  Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
+  The first tempo and first meter are special. they must move together, and must be locked to audio.
+  Audio locked tempos which lie before the first meter are made inactive.
+  They will be re-activated if the first meter is again placed before them.
 
   With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
   referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
@@ -614,18 +617,13 @@ MeterSection::get_state() const
   Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
   Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
 
-  The first tempo and first meter are special. they must move together, and must be locked to audio.
-  Audio locked tempos which lie before the first meter are made inactive.
-  They will be re-activated if the first meter is again placed before them.
-
-  Both tempos and meters have a pulse position and a frame position.
-  Meters also have a beat position, which is always 0.0 for the first meter.
-
-  A tempo locked to music is locked to musical pulses.
-  A meter locked to music is locked to beats.
-
-  Recomputing the tempo map is the process where the 'missing' position
+  Recomputing the map is the process where the 'missing' position
   (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
+  We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
+  We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
+
+  Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
+  and querying its appropriate meter/tempo.
 
   It is important to keep the _metrics in an order that makes sense.
   Because ramped MusicTime and AudioTime tempos can interact with each other,
@@ -1415,6 +1413,7 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame)
        }
        const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
 
+       /* audio locked meters fake their beat */
        if (next_m && next_m->beat() < beat) {
                return next_m->beat();
        }
@@ -1433,20 +1432,10 @@ TempoMap::frame_at_beat (const double& beat) const
 framecnt_t
 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
-       const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
-       MeterSection* prev_m = 0;
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_m && m->beat() > beat) {
-                               break;
-                       }
-                       prev_m = m;
-               }
-       }
+       const TempoSection* prev_t = &tempo_section_at_beat_locked (metrics, beat);
+       const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
 
-       return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
+       return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
 }
 
 Tempo
@@ -1461,9 +1450,7 @@ TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame
 {
        TempoSection* prev_t = 0;
 
-       Metrics::const_iterator i;
-
-       for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+       for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
                TempoSection* t;
                if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
                        if (!t->active()) {
@@ -1535,6 +1522,20 @@ TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con
        return prev_t->frame();
 }
 
+/** more precise than doing tempo_at_frame (frame_at_beat (b)),
+ *  as there is no intermediate frame rounding.
+ */
+Tempo
+TempoMap::tempo_at_beat (const double& beat) const
+{
+       Glib::Threads::RWLock::ReaderLock lm (lock);
+       const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
+       const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
+       const double note_type = prev_t->note_type();
+
+       return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
+}
+
 double
 TempoMap::pulse_at_beat (const double& beat) const
 {
@@ -1545,20 +1546,9 @@ TempoMap::pulse_at_beat (const double& beat) const
 double
 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
 {
-       MeterSection* prev_m = 0;
-
-       for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
-               MeterSection* m;
-               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
-                       if (prev_m && m->beat() > beat) {
-                               break;
-                       }
-                       prev_m = m;
-               }
+       const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
 
-       }
-       double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
-       return ret;
+       return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
 }
 
 double
@@ -1870,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
 {
@@ -1941,7 +1943,7 @@ TempoMap::check_solved (const Metrics& metrics) const
                                /* gradient limit - who knows what it should be?
                                   things are also ok (if a little chaotic) without this
                                */
-                               if (fabs (prev_t->c_func()) > 200.0) {
+                               if (fabs (prev_t->c_func()) > 1000.0) {
                                        //std::cout << "c : " << prev_t->c_func() << std::endl;
                                        return false;
                                }
@@ -1998,6 +2000,7 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram
        TempoSection* section_prev = 0;
        framepos_t first_m_frame = 0;
 
+       /* can't move a tempo before the first meter */
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
                if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
@@ -2024,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) {
@@ -2047,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);
@@ -2108,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);
@@ -2151,10 +2165,8 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                }
        }
 
-       /* it would make sense to bail out if there is no audio-locked meter,
-          however it may be desirable to move a music-locked meter by frame at some point.
-       */
        TempoSection* meter_locked_tempo = 0;
+
        for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
                TempoSection* t;
                if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
@@ -2208,7 +2220,10 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram
                                                        return false;
                                                }
                                        } else {
-                                               /* all is ok. set section's tempo */
+                                               /* all is ok. set section's locked tempo if allowed.
+                                                  possibly disallowed if there is an adjacent audio-locked tempo.
+                                                  XX this check could possibly go. its never actually happened here.
+                                               */
                                                MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
                                                meter_copy->set_frame (frame);
 
@@ -2513,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);
@@ -2540,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);
@@ -2656,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();
@@ -2691,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();
@@ -2705,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);
@@ -2926,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);
@@ -3018,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);
@@ -3516,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 */
@@ -3557,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 {