Tempo ramps - rework mouse dragging of tempo marks, derive beat_at_tempo() and tempo_...
authornick_m <mainsbridge@gmail.com>
Thu, 3 Mar 2016 13:08:21 +0000 (00:08 +1100)
committernick_m <mainsbridge@gmail.com>
Fri, 27 May 2016 13:38:11 +0000 (23:38 +1000)
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_drag.h
libs/ardour/ardour/tempo.h
libs/ardour/tempo.cc

index b8339cc557f4e302ad1a841ac0cef6e1776126d4..00ebdeb12543286c29d28f3b7d087c4b89cf3979 100644 (file)
@@ -3122,6 +3122,8 @@ MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
        DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
        _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
        assert (_marker);
+       _real_section = &_marker->meter();
+
 }
 
 void
@@ -3151,15 +3153,15 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                // leave or lose the original marker (leave if its a copy; lose if its
                // not, because we'll remove it from the map).
 
+               char name[64];
+               snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
+
                MeterSection section (_marker->meter());
 
                if (!section.movable()) {
                        return;
                }
 
-               char name[64];
-               snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
-
                _marker = new MeterMarker (
                        *_editor,
                        *_editor->meter_group,
@@ -3172,17 +3174,22 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
 
                if (!_copy) {
+                       _editor->begin_reversible_command (_("move meter mark"));
                        TempoMap& map (_editor->session()->tempo_map());
                        /* get current state */
                        before_state = &map.get_state();
                        /* remove the section while we drag it */
-                       map.remove_meter (section, true);
+                       //map.remove_meter (section, true);
                }
+               _marker->hide();
        }
 
-       framepos_t const pf = adjusted_current_frame (event);
+       framepos_t const pf = adjusted_current_frame (event, false);
+       double const baf = _editor->session()->tempo_map().beat_at_frame (pf);
 
        _marker->set_position (pf);
+       _editor->session()->tempo_map().gui_move_meter (_real_section, _marker->meter(), pf, baf);
+
        show_verbose_cursor_time (pf);
 }
 
@@ -3222,8 +3229,6 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                _editor->commit_reversible_command ();
 
        } else {
-               _editor->begin_reversible_command (_("move meter mark"));
-
                /* we removed it before, so add it back now */
                if (_marker->meter().position_lock_style() == AudioTime) {
                        map.add_meter (_marker->meter(), _marker->position());
@@ -3325,9 +3330,9 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
 
        framepos_t const pf = adjusted_current_frame (event, false);
        double const baf = _editor->session()->tempo_map().beat_at_frame (pf);
-
-       _marker->set_position (adjusted_current_frame (event, false));
-       _editor->session()->tempo_map().gui_set_tempo_frame (*_real_section, pf, baf);
+       Tempo const tp = _marker->tempo();
+       _marker->set_position (pf);
+       _editor->session()->tempo_map().gui_move_tempo (_real_section, tp, pf, baf);
 
        show_verbose_cursor_time (pf);
 }
index bd2672e23e3c3189b8021d5042343c5bc2d71d49..26029299ced8c0da71530d146358e275ef7e663a 100644 (file)
@@ -705,6 +705,8 @@ public:
 
 private:
        MeterMarker* _marker;
+       ARDOUR::MeterSection* _real_section;
+
        bool _copy;
        XMLNode* before_state;
 };
index 4396cb8f992f4ca68279d1f043822629bb50119a..060507676db6463ae9165557e1e74dcd84e9c264 100644 (file)
@@ -185,6 +185,9 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        double tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const;
        framepos_t frame_at_tempo (double tempo, framecnt_t frame_rate) const;
 
+       double tempo_at_beat (double beat) const;
+       double beat_at_tempo (double tempo) const;
+
        double tick_at_frame (framepos_t frame, framecnt_t frame_rate) const;
        framepos_t frame_at_tick (double tick, framecnt_t frame_rate) const;
 
@@ -193,6 +196,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
 
        void set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate);
        double compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const;
+
        double get_c_func () const { return _c_func; }
        void set_c_func (double c_func) { _c_func = c_func; }
 
@@ -213,6 +217,9 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
        double tick_tempo_at_time (double time) const;
        double time_at_tick_tempo (double tick_tempo) const;
 
+       double tick_tempo_at_tick (double tick) const;
+       double tick_at_tick_tempo (double tick_tempo) const;
+
        double tick_at_time (double time) const;
        double time_at_tick (double tick) const;
 
@@ -369,10 +376,15 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
        void remove_meter (const MeterSection&, bool send_signal);
 
        framepos_t compute_replacement_tempo_section (TempoSection* section, const double& tempo, const double& beat);
+       Metrics imagine_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame, const double& beat);
+       Metrics imagine_new_order (MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat);
+
        void replace_tempo (const TempoSection&, const Tempo&, const double& where, TempoSection::Type type);
-       void replace_tempo (const TempoSection&, const Tempo&, const framepos_t& where, TempoSection::Type type);
+       void replace_tempo (const TempoSection&, const Tempo&, const framepos_t& frame, TempoSection::Type type);
+
+       void gui_move_tempo (TempoSection*, const Tempo& bpm, const framepos_t& where, const double& beat);
+       void gui_move_meter (MeterSection*, const Meter& mt, const framepos_t& where, const double& beat);
 
-       void gui_set_tempo_frame (TempoSection&, framepos_t where, double beat);
        void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where);
        void replace_meter (const MeterSection&, const Meter&, const framepos_t& frame);
 
index e2a2efd91e9aa98cd6102856f35c4aef99d20b2e..c263f37387946bfd92e1fbd938c925badc68b0ea 100644 (file)
@@ -216,7 +216,7 @@ TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
 }
 
 /** returns the zero-based frame (relative to session)
-   where the tempo occurs.
+   where the tempo occurs in this section. note that the tempo map may have multiple such values.
 */
 framepos_t
 TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
@@ -227,6 +227,31 @@ TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
 
        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) frame.
+*/
+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 frame (relative to session)
+   where the tempo occurs in this section. note that the tempo map may have multiple such values.
+*/
+double
+TempoSection::beat_at_tempo (double bpm) const
+{
+       if (_type == Constant) {
+               return 0;
+       }
+
+       return (tick_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat) + beat();
+}
 
 
 /** returns the zero-based tick (relative to session origin)
@@ -396,6 +421,20 @@ 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
@@ -947,89 +986,142 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const frame
        PropertyChanged (PropertyChange ());
 }
 
-void
-TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
+Metrics
+TempoMap::imagine_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame, const double& beat)
 {
-       {
-               Glib::Threads::RWLock::WriterLock lm (lock);
+       Metrics imaginary (metrics);
 
-               if (ts.position_lock_style() == MusicTime) {
-                       std::cerr << "Music " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
-                       /* MusicTime */
-                       ts.set_beat (beat_where);
-                       Metrics::const_iterator i;
-
-                       TempoSection* prev_ts = 0;
-                       MetricSectionSorter cmp;
-                       metrics.sort (cmp);
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+       TempoSection* prev_ts = 0;
+       TempoSection* t;
 
-                                       if (t->beat() >= beat_where) {
-                                               break;
-                                       }
+       /*set frame and sort */
+       section->set_frame (frame);
+       MetricSectionFrameSorter fcmp;
+       imaginary.sort (fcmp);
 
+       /* recompute */
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
+                               if (t == section) {
+                                       /* we have already set the frame - set the beat */
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
+                                       section->set_beat (prev_ts->beat_at_frame (frame, _frame_rate));
                                        prev_ts = t;
+                                       continue;
+                               }
+                               if (t->position_lock_style() == MusicTime) {
+                                       prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
+                                       t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
+                               } else {
+                                       prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
+                                       t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
                                }
                        }
+                       prev_ts = t;
+               }
+       }
+       /* to do - check precision using _at_tempo() methods */
+/*
+       prev_ts = 0;
+       std::cerr << "dumping imaginary order ------" << std::endl;;
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+                       if (prev_ts) {
 
-                       prev_ts->set_c_func_from_tempo_and_beat (ts.beats_per_minute(), ts.beat(), _frame_rate);
-                       ts.set_frame (prev_ts->frame_at_beat (ts.beat(), _frame_rate));
-               } else {
-                       std::cerr << "Audio " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
+                               std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
+                               std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
+                               std::cerr << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute()) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), _frame_rate) << std::endl;
+                               std::cerr << "   ------" << std::endl;;
 
-                       /*AudioTime*/
-                       ts.set_frame (frame);
-                       MetricSectionFrameSorter fcmp;
-                       metrics.sort (fcmp);
+                       }
+                       prev_ts = t;
+               }
+       }
+       std::cerr << "end dump ------";
+*/
+       return imaginary;
+}
 
-                       Metrics::const_iterator i;
-                       TempoSection* prev_ts = 0;
-                       TempoSection* next_ts = 0;
+Metrics
+TempoMap::imagine_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat)
+{
+       /* incomplete */
+       Metrics imaginary (metrics);
 
-                       for (i = metrics.begin(); i != metrics.end(); ++i) {
-                               TempoSection* t;
-                               if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-                                       if (t->frame() == frame) {
-                                               continue;
-                                       }
-                                       if (frame < t->frame()) {
-                                               next_ts = t;
-                                               break;
-                                       }
+       MeterSection* prev_ms = 0;
+       MeterSection* m;
+       MeterSection* our_section = 0;
+       framepos_t ret = 0;
+       MetricSectionSorter cmp;
 
-                                       prev_ts = t;
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               if (section->beat() == m->beat()) {
+                                       our_section = m;
+                                       continue;
+                               }
+                               if (beat < m->beat()){
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
+                                       our_section->set_beat (b_bbt);
+                                       our_section->set_frame (frame_at_beat_locked (beat));
+                                       break;
                                }
-                       }
 
-                       if (prev_ts) {
-                               /* set the start beat - we need to reset the function constant before beat calculations make sense*/
-                               prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame, _frame_rate));
-                               double beats = prev_ts->beat_at_frame (frame, _frame_rate);
-
-                               if (next_ts) {
-                                       if (next_ts->beat() < beats) {
-                                               /* with frame-based editing, it is possible to get in a
-                                                  situation where if the tempo was placed at the mouse pointer frame,
-                                                  the following music-based tempo would jump to an earlier frame,
-                                                  changing the odering.
-                                                  in this case, we need some kind of tempo map speculator.
-                                               */
-                                       } else if (prev_ts->beat() > beats) {
-                                               ts.set_beat (prev_ts->beat());
-                                       } else {
-                                               ts.set_beat (beats);
-                                       }
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_beat_locked (m->beat()));
                                } else {
-                                       ts.set_beat (beats);
-                                       ts.set_c_func (0.0);
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
+                                       m->set_beat (b_bbt);
+                               }
+                       }
+                       prev_ms = m;
+               }
+       }
+       /* now we do the whole thing again because audio-locked sections will have caused a re-order */
+       prev_ms = 0;
+       metrics.sort (cmp);
 
+       for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+               if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+                       if (prev_ms) {
+                               if (section->beat() == m->beat()) {
+                                       continue;
+                               }
+                               if (beat < m->beat()){
+                                       ret = frame_at_beat (beat);
+                                       section->set_frame (ret);
+                                       prev_ms = section;
+                                       break;
+                               }
+                               if (m->position_lock_style() == MusicTime) {
+                                       m->set_frame (frame_at_beat (m->beat()));
+                               } else {
+                                       pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
+                                       m->set_beat (b_bbt);
                                }
-                               MetricSectionSorter cmp;
-                               metrics.sort (cmp);
                        }
+                       prev_ms = m;
                }
+       }
+
+       if (!ret) {
+               pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
+               section->set_beat (b_bbt);
+               section->set_frame (frame_at_beat_locked (beat));
+       }
+       return imaginary;
+}
+
+void
+TempoMap::gui_move_tempo (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame, const double& beat_where)
+{
+       {
+               Glib::Threads::RWLock::WriterLock lm (lock);
+               Metrics new_order = imagine_new_order (ts, bpm, frame, beat_where);
+               metrics.clear();
+               metrics = new_order;
 
                recompute_map (false);
        }
@@ -1037,6 +1129,13 @@ TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_
        MetricPositionChanged (); // Emit Signal
 }
 
+void
+TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame, const double&  beat_where)
+{
+       Glib::Threads::RWLock::WriterLock lm (lock);
+       Metrics imaginary = imagine_new_order (ms, mt, frame, beat_where);
+}
+
 void
 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
 {