AutomationLine time-unit conversion and paste API update
authorRobin Gareus <robin@gareus.org>
Wed, 26 Apr 2017 14:21:39 +0000 (16:21 +0200)
committerRobin Gareus <robin@gareus.org>
Wed, 26 Apr 2017 21:37:27 +0000 (23:37 +0200)
This fixes copy/paste of MIDI automation (time-unit: beat) from/to
Parameter automation (time-unit: samples).

It also fixes repeatedly pasting with tempo-ramps: pre-multiply length
before converting to samples.

gtk2_ardour/automation_region_view.cc
gtk2_ardour/automation_time_axis.cc
libs/ardour/ardour/automation_list.h
libs/ardour/automation_list.cc
libs/evoral/evoral/ControlList.hpp
libs/evoral/src/ControlList.cpp

index e3e41b442801028c6eb47aefc01709749e747c52..ce1cd1498d281b2d57ae1f2eac24c85aaa7e1787 100644 (file)
@@ -210,6 +210,8 @@ AutomationRegionView::paste (framepos_t                                      pos
                              float                                           times,
                              boost::shared_ptr<const ARDOUR::AutomationList> slist)
 {
+       using namespace ARDOUR;
+
        AutomationTimeAxisView* const             view    = automation_view();
        boost::shared_ptr<ARDOUR::AutomationList> my_list = _line->the_list();
 
@@ -218,15 +220,24 @@ AutomationRegionView::paste (framepos_t                                      pos
                return false;
        }
 
+       AutomationType src_type = (AutomationType)slist->parameter().type ();
+       double len = slist->length();
+
        /* add multi-paste offset if applicable */
-       pos += view->editor().get_paste_offset(
-               pos, paste_count, _source_relative_time_converter.to(slist->length()));
+       if (parameter_is_midi (src_type)) {
+               // convert length to samples (incl tempo-ramps)
+               len = DoubleBeatsFramesConverter (view->session()->tempo_map(), pos).to (len * paste_count);
+               pos += view->editor ().get_paste_offset (pos, paste_count > 0 ? 1 : 0, len);
+       } else {
+               pos += view->editor ().get_paste_offset (pos, paste_count, len);
+       }
 
-       const double model_pos = _source_relative_time_converter.from(
+       /* convert sample-position to model's unit and position */
+       const double model_pos = _source_relative_time_converter.from (
                pos - _source_relative_time_converter.origin_b());
 
        XMLNode& before = my_list->get_state();
-       my_list->paste(*slist, model_pos, times);
+       my_list->paste(*slist, model_pos, DoubleBeatsFramesConverter (view->session()->tempo_map(), pos));
        view->session()->add_command(
                new MementoCommand<ARDOUR::AutomationList>(_line->memento_command_binder(), &before, &my_list->get_state()));
 
index cb8456e062386ed035693b79c6981bc96a26073d..0b71e423cd98d8e2e95d736a9db9e4902557865d 100644 (file)
@@ -29,6 +29,7 @@
 #include "pbd/types_convert.h"
 
 #include "ardour/automation_control.h"
+#include "ardour/beats_frames_converter.h"
 #include "ardour/event_type_map.h"
 #include "ardour/parameter_types.h"
 #include "ardour/profile.h"
@@ -695,12 +696,23 @@ AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float t
        counts.increase_n_lines(_parameter);
 
        /* add multi-paste offset if applicable */
-       pos += _editor.get_paste_offset(pos, paste_count, (*p)->length());
 
+       AutomationType src_type = (AutomationType)(*p)->parameter().type ();
+       double len = (*p)->length();
+
+       if (parameter_is_midi (src_type)) {
+               // convert length to samples (incl tempo-ramps)
+               len = DoubleBeatsFramesConverter (_session->tempo_map(), pos).to (len * paste_count);
+               pos += _editor.get_paste_offset (pos, paste_count > 0 ? 1 : 0, len);
+       } else {
+               pos += _editor.get_paste_offset (pos, paste_count, len);
+       }
+
+       /* convert sample-position to model's unit and position */
        double const model_pos = _line->time_converter().from (pos - _line->time_converter().origin_b ());
 
        XMLNode &before = alist->get_state();
-       alist->paste (**p, model_pos, times);
+       alist->paste (**p, model_pos, DoubleBeatsFramesConverter (_session->tempo_map(), pos));
        _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
 
        return true;
index ca72f5f01f7001fe3958e2820eb64bcb2e466bd0..3f4d3f2f525737207e9e1d7edd9deb825d8a783f 100644 (file)
@@ -40,6 +40,7 @@
 namespace ARDOUR {
 
 class AutomationList;
+class DoubleBeatsFramesConverter;
 
 /** A SharedStatefulProperty for AutomationLists */
 class LIBARDOUR_API AutomationListProperty : public PBD::SharedStatefulProperty<AutomationList>
@@ -81,6 +82,7 @@ class LIBARDOUR_API AutomationList : public Evoral::ControlList, public PBD::Sta
        AutomationList& operator= (const AutomationList&);
 
        void thaw ();
+       bool paste (const ControlList&, double, DoubleBeatsFramesConverter const&);
 
        void set_automation_state (AutoState);
        AutoState automation_state() const { return _state; }
index c397323767ee435c55c40f3ceb64cee87a1905da..0d43298610ad69aeaa3daeff24d9ecc7b30649bc 100644 (file)
 #include <sstream>
 #include <algorithm>
 #include "ardour/automation_list.h"
+#include "ardour/beats_frames_converter.h"
 #include "ardour/event_type_map.h"
 #include "ardour/parameter_descriptor.h"
+#include "ardour/parameter_types.h"
 #include "ardour/evoral_types_convert.h"
 #include "ardour/types_convert.h"
 #include "evoral/Curve.hpp"
@@ -289,6 +291,31 @@ AutomationList::thaw ()
        }
 }
 
+bool
+AutomationList::paste (const ControlList& alist, double pos, DoubleBeatsFramesConverter const& bfc)
+{
+       AutomationType src_type = (AutomationType)alist.parameter().type();
+       AutomationType dst_type = (AutomationType)_parameter.type();
+
+       if (parameter_is_midi (src_type) == parameter_is_midi (dst_type)) {
+               return ControlList::paste (alist, pos);
+       }
+       bool to_frame = parameter_is_midi (src_type);
+
+       ControlList cl (alist);
+       cl.clear ();
+       for (const_iterator i = alist.begin ();i != alist.end (); ++i) {
+               double when = (*i)->when;
+               if (to_frame) {
+                       when = bfc.to ((*i)->when);
+               } else {
+                       when = bfc.from ((*i)->when);
+               }
+               cl.fast_simple_add (when, (*i)->value);
+       }
+       return ControlList::paste (cl, pos);
+}
+
 Command*
 AutomationList::memento_command (XMLNode* before, XMLNode* after)
 {
index 8e72c21c48d7863c11c8e68c3a432e2334217d2d..b58c186c216c5fa1be2c293e6c46a7e4f229431e 100644 (file)
@@ -172,7 +172,7 @@ public:
         */
        void clear (double start, double end);
 
-       bool paste (const ControlList&, double position, float times);
+       bool paste (const ControlList&, double position);
 
        void set_yrange (double min, double max) {
                _min_yval = min;
index 0b2184a97280f3181b8a754b21c1e017730a546c..ce8ea89fc9d347aa657e7fa3e212e3556dbe26ca 100644 (file)
@@ -1652,7 +1652,7 @@ ControlList::clear (double start, double end)
 
 /** @param pos Position in model coordinates */
 bool
-ControlList::paste (const ControlList& alist, double pos, float /*times*/)
+ControlList::paste (const ControlList& alist, double pos)
 {
        if (alist._events.empty()) {
                return false;