Tempo ramps - reinstate cross-dragging of music-locked meters, various bug fixes.
authornick_m <mainsbridge@gmail.com>
Tue, 3 May 2016 12:15:10 +0000 (22:15 +1000)
committernick_m <mainsbridge@gmail.com>
Fri, 27 May 2016 13:38:16 +0000 (23:38 +1000)
- revert failed frameoffset_t experiment
- caclulate meters using bbt
- fix tempo dilation when first tempo is ramped.

gtk2_ardour/editor_drag.cc
libs/ardour/ardour/tempo.h
libs/ardour/tempo.cc

index 080e82d98414c215e4f96a4f90134cc6826621d1..a6867892537ac7ebc43bcac86a5045a12dd8323a 100644 (file)
@@ -3204,7 +3204,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
                        /* adjust previous tempo to match meter frame */
                        _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf);
-               } else if ((bbt.bars > _real_section->bbt().bars && pf > last_pointer_frame())
+               } else if ((bbt.bars != _real_section->bbt().bars && pf > last_pointer_frame())
                           || (bbt.bars < _real_section->bbt().bars && pf < last_pointer_frame())) {
                        /* move meter beat-based */
                        _editor->session()->tempo_map().gui_move_meter (_real_section, bbt);
index 87023e0cd8af50d6179541f885cc2d1ec0ab6d2e..889fde3e99c2ae5ff455f124a3c082aa00e0457c 100644 (file)
@@ -199,8 +199,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        bool locked_to_meter ()  const { return _locked_to_meter; }
        void set_locked_to_meter (bool yn) { _locked_to_meter = yn; }
 
-       double tempo_at_frame (const frameoffset_t& frame, const framecnt_t& frame_rate) const;
-       frameoffset_t frame_at_tempo (const double& ppm, const double& beat, const framecnt_t& frame_rate) const;
+       double tempo_at_frame (const framepos_t& frame, const framecnt_t& frame_rate) const;
+       framepos_t frame_at_tempo (const double& ppm, const double& beat, const framecnt_t& frame_rate) const;
 
        double tempo_at_pulse (const double& pulse) const;
        double pulse_at_tempo (const double& ppm, const framepos_t& frame, const framecnt_t& frame_rate) const;
@@ -215,8 +215,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
 
   private:
 
-       frameoffset_t minute_to_frame (const double& time, const framecnt_t& frame_rate) const;
-       double frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const;
+       framepos_t minute_to_frame (const double& time, const framecnt_t& frame_rate) const;
+       double frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const;
 
        /*  tempo ramp functions. zero-based with time in minutes,
         * 'tick tempo' in ticks per minute and tempo in bpm.
index a9707c85ef2f44541d6175b1e89870f3824d402f..8af5ab3fbebe98621acafd6c896861de51943a11 100644 (file)
@@ -207,14 +207,14 @@ TempoSection::set_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 frameoffset_t& f, const framecnt_t& frame_rate) const
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
 {
 
        if (_type == Constant || _c_func == 0.0) {
                return pulses_per_minute();
        }
 
-       return pulse_tempo_at_time (frame_to_minute (f - (frameoffset_t) frame(), frame_rate));
+       return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
 }
 
 /** returns the zero-based frame (relative to session)
@@ -222,7 +222,7 @@ TempoSection::tempo_at_frame (const frameoffset_t& f, const framecnt_t& frame_ra
    beat b is only used for constant tempos.
    note that the tempo map may have multiple such values.
 */
-frameoffset_t
+framepos_t
 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
 {
        if (_type == Constant || _c_func == 0.0) {
@@ -277,11 +277,11 @@ TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate)
    falls.
 */
 
-frameoffset_t
+framepos_t
 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
 {
        if (_type == Constant || _c_func == 0.0) {
-               return (frameoffset_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+               return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
        }
 
        return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
@@ -380,14 +380,14 @@ TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end
        return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
 }
 
-frameoffset_t
+framepos_t
 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
 {
-       return (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5);
+       return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
 }
 
 double
-TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const
+TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
 {
        return (frame / (double) frame_rate) / 60.0;
 }
@@ -762,7 +762,6 @@ TempoMap::do_insert (MetricSection* section)
         */
        MeterSection* m = 0;
        if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
-               //assert (m->bbt().ticks == 0);
 
                if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
 
@@ -1321,7 +1320,12 @@ TempoMap::recompute_tempos (Metrics& metrics)
        prev_t->set_c_func (0.0);
 }
 
-/* tempos must be positioned correctly */
+/* tempos must be positioned correctly.
+   the current approach is to use a meter's bbt time as its base position unit.
+   this means that a meter's beat may change, but its bbt may not.
+   an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
+   while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
+*/
 void
 TempoMap::recompute_meters (Metrics& metrics)
 {
@@ -1333,29 +1337,62 @@ TempoMap::recompute_meters (Metrics& metrics)
                        if (meter->position_lock_style() == AudioTime) {
                                double pulse = 0.0;
                                pair<double, BBT_Time> b_bbt;
-                               if (meter->movable()) {
-                                       b_bbt = make_pair (meter->beat(), meter->bbt());
-                                       pulse = pulse_at_frame_locked (metrics, meter->frame());
+                               TempoSection* meter_locked_tempo = 0;
+                               for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
+                                       TempoSection* t;
+                                       if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
+                                                       meter_locked_tempo = t;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               if (prev_m) {
+                                       const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+                                       if (beats + prev_m->beat() != meter->beat()) {
+                                               /* reordering caused a bbt change */
+                                               b_bbt = make_pair (beats + prev_m->beat()
+                                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                               pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+
+                                       } else if (meter->movable()) {
+                                               b_bbt = make_pair (meter->beat(), meter->bbt());
+                                               pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+                                       }
                                } else {
                                        b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                }
+                               if (meter_locked_tempo) {
+                                       meter_locked_tempo->set_pulse (pulse);
+                                       recompute_tempos (metrics);
+                               }
                                meter->set_beat (b_bbt);
                                meter->set_pulse (pulse);
+
                        } else {
+                               /* MusicTime */
                                double pulse = 0.0;
                                pair<double, BBT_Time> new_beat;
                                if (prev_m) {
-                                       pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
-                                       //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
+                                       const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+                                       if (beats + prev_m->beat() != meter->beat()) {
+                                               /* reordering caused a bbt change */
+                                               new_beat = make_pair (beats + prev_m->beat()
+                                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                                       } else {
+                                               new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
+                                       }
+                                       pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                } else {
                                        /* shouldn't happen - the first is audio-locked */
                                        pulse = pulse_at_beat_locked (metrics, meter->beat());
                                        new_beat = make_pair (meter->beat(), meter->bbt());
                                }
 
-                               //meter->set_beat (new_beat);
-                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+                               meter->set_beat (new_beat);
                                meter->set_pulse (pulse);
+                               meter->set_frame (frame_at_pulse_locked (metrics, pulse));
                        }
 
                        prev_m = meter;
@@ -1866,7 +1903,8 @@ TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
                        if (prev_m && m->position_lock_style() == AudioTime) {
                                TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
                                const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
-                               const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
+                               const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
+
                                if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
                                        return false;
                                }
@@ -1968,7 +2006,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
                recompute_tempos (imaginary);
        }
 
-       recompute_meters (imaginary);
        if (check_solved (imaginary, true)) {
                return true;
        }
@@ -1984,7 +2021,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
                recompute_tempos (imaginary);
        }
 
-       recompute_meters (imaginary);
        if (check_solved (imaginary, true)) {
                return true;
        }
@@ -2045,7 +2081,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
                recompute_tempos (imaginary);
        }
 
-       recompute_meters (imaginary);
        if (check_solved (imaginary, false)) {
                return true;
        }
@@ -2061,7 +2096,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
                recompute_tempos (imaginary);
        }
 
-       recompute_meters (imaginary);
        if (check_solved (imaginary, false)) {
                return true;
        }
@@ -2087,10 +2121,13 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
                }
        }
 
+       /* 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 i = imaginary.begin(); i != imaginary.end(); ++i) {
+       for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
                TempoSection* t;
-               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+               if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
                        if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
                                meter_locked_tempo = t;
                                break;
@@ -2231,7 +2268,18 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
 bool
 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
 {
+       /* disallow setting section to an existing meter's bbt */
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               MeterSection* m;
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (m->bbt().bars == when.bars) {
+                               return false;
+                       }
+               }
+       }
+
        MeterSection* prev_m = 0;
+       MeterSection* section_prev = 0;
 
        for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
                MeterSection* m;
@@ -2239,25 +2287,32 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time&
                        pair<double, BBT_Time> b_bbt;
                        double new_pulse = 0.0;
 
-                       if (prev_m && m == section){
-                               const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
-                               const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
-
-                               b_bbt = make_pair (beats + prev_m->beat(), when);
+                       if (prev_m && m->bbt().bars > when.bars && !section_prev){
+                               section_prev = prev_m;
+                               const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
+                               const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
+                               pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
 
                                section->set_beat (b_bbt);
                                section->set_pulse (pulse);
                                section->set_frame (frame_at_pulse_locked (imaginary, pulse));
-
-                               prev_m = m;
+                               prev_m = section;
                                continue;
-
-                       } else if (m->bbt().bars == when.bars) {
-                               return false;
                        }
 
                        if (m->position_lock_style() == AudioTime) {
-                               if (m->movable()) {
+                               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) {
+                                               if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
+                                                       meter_locked_tempo = t;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               if (prev_m) {
                                        const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
 
                                        if (beats + prev_m->beat() != m->beat()) {
@@ -2265,25 +2320,32 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time&
                                                b_bbt = make_pair (beats + prev_m->beat()
                                                                   , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
                                                new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
-                                       } else {
+                                       } else if (m->movable()) {
                                                b_bbt = make_pair (m->beat(), m->bbt());
-                                               new_pulse = pulse_at_frame_locked (imaginary, m->frame());
+                                               new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
                                        }
                                } else {
                                        b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
                                }
-
+                               if (meter_locked_tempo) {
+                                       meter_locked_tempo->set_pulse (new_pulse);
+                                       recompute_tempos (imaginary);
+                               }
                                m->set_beat (b_bbt);
                                m->set_pulse (new_pulse);
 
                        } else {
+                               /* MusicTime */
                                const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
-
-                               b_bbt = make_pair (beats + prev_m->beat()
-                                                  , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
-                               new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar()
-                                                              / prev_m->note_divisor());
-
+                               if (beats + prev_m->beat() != m->beat()) {
+                                       /* tempo/ meter change caused a change in beat (bar). */
+                                       b_bbt = make_pair (beats + prev_m->beat()
+                                                          , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+                               } else {
+                                       b_bbt = make_pair (beats + prev_m->beat()
+                                                          , m->bbt());
+                               }
+                               new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
                                m->set_beat (b_bbt);
                                m->set_pulse (new_pulse);
                                m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
@@ -2293,6 +2355,17 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time&
                }
        }
 
+       if (!section_prev) {
+
+               const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+               const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
+               pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
+
+               section->set_beat (b_bbt);
+               section->set_pulse (pulse);
+               section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+       }
+
        MetricSectionSorter cmp;
        imaginary.sort (cmp);
        if (section->position_lock_style() == AudioTime) {
@@ -2303,6 +2376,7 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time&
        } else {
                recompute_meters (imaginary);
        }
+
        return true;
 }
 
@@ -2520,6 +2594,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
                MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
                if (solve_map (future_map, copy, frame)) {
                        solve_map (_metrics, ms, frame);
+                       recompute_tempos (_metrics);
                }
        }
 
@@ -2541,6 +2616,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
                MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
                if (solve_map (future_map, copy, bbt)) {
                        solve_map (_metrics, ms, bbt);
+                       recompute_tempos (_metrics);
                }
        }
 
@@ -2592,6 +2668,7 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
                /* disabled for now due to faked tempo locked to meter pulse */
                return;
        }
+
        {
                Glib::Threads::RWLock::WriterLock lm (lock);
                ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
@@ -2603,7 +2680,7 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
                const frameoffset_t fr_off = frame - ms->frame();
                double new_bpm = 0.0;
 
-               if (prev_t) {
+               if (prev_t && prev_t->pulse() > 0.0) {
                        prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
                }
 
@@ -2709,7 +2786,6 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
        }
 
        MetricPositionChanged (); // Emit Signal
-       return;
 }
 
 framecnt_t