OMNIBUS COMMIT: prefer const XMLNode::property method (and provide a real one)
[ardour.git] / libs / ardour / automatable.cc
index 310592ae4babfccf982872186eb754eb3692b025..f85bc83e8549a49e78ce806cb4edc444ee5cc2fd 100644 (file)
 
 */
 
-#include <fstream>
 #include <cstdio>
 #include <errno.h>
 
+#include "pbd/gstdio_compat.h"
 #include <glibmm/miscutils.h>
 
 #include "pbd/error.h"
@@ -28,6 +28,7 @@
 #include "ardour/amp.h"
 #include "ardour/automatable.h"
 #include "ardour/event_type_map.h"
+#include "ardour/gain_control.h"
 #include "ardour/midi_track.h"
 #include "ardour/pan_controllable.h"
 #include "ardour/pannable.h"
@@ -66,7 +67,7 @@ Automatable::~Automatable ()
 {
        {
                Glib::Threads::Mutex::Lock lm (_control_lock);
-               
+
                for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
                        boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
                }
@@ -76,7 +77,7 @@ Automatable::~Automatable ()
 int
 Automatable::old_set_automation_state (const XMLNode& node)
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        if ((prop = node.property ("path")) != 0) {
                load_automation (prop->value());
@@ -98,7 +99,8 @@ Automatable::load_automation (const string& path)
                fullpath = _a_session.automation_dir();
                fullpath += path;
        }
-       ifstream in (fullpath.c_str());
+
+       FILE * in = g_fopen (fullpath.c_str (), "rb");
 
        if (!in) {
                warning << string_compose(_("cannot open %2 to load automation data (%3)")
@@ -110,14 +112,17 @@ Automatable::load_automation (const string& path)
        set<Evoral::Parameter> tosave;
        controls().clear ();
 
-       while (in) {
+       while (!feof(in)) {
                double when;
                double value;
                uint32_t port;
 
-               in >> port;  if (!in) break;
-               in >> when;  if (!in) goto bad;
-               in >> value; if (!in) goto bad;
+               if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
+                       if (feof(in)) {
+                               break;
+                       }
+                       goto bad;
+               }
 
                Evoral::Parameter param(PluginAutomation, 0, port);
                /* FIXME: this is legacy and only used for plugin inserts?  I think? */
@@ -125,12 +130,14 @@ Automatable::load_automation (const string& path)
                c->list()->add (when, value);
                tosave.insert (param);
        }
+       ::fclose (in);
 
        return 0;
 
   bad:
        error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
        controls().clear ();
+       ::fclose (in);
        return -1;
 }
 
@@ -163,6 +170,8 @@ Automatable::describe_parameter (Evoral::Parameter param)
 
        if (param == Evoral::Parameter(GainAutomation)) {
                return _("Fader");
+       } else if (param.type() == TrimAutomation) {
+               return _("Trim");
        } else if (param.type() == MuteAutomation) {
                return _("Mute");
        } else if (param.type() == MidiCCAutomation) {
@@ -211,7 +220,7 @@ Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter le
 
                if ((*niter)->name() == "AutomationList") {
 
-                       const XMLProperty* id_prop = (*niter)->property("automation-id");
+                       XMLProperty const * id_prop = (*niter)->property("automation-id");
 
                        Evoral::Parameter param = (id_prop
                                        ? EventTypeMap::instance().from_symbol(id_prop->value())
@@ -276,6 +285,7 @@ Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState
        if (c && (s != c->automation_state())) {
                c->set_automation_state (s);
                _a_session.set_dirty ();
+               AutomationStateChanged(); /* Emit signal */
        }
 }
 
@@ -285,7 +295,7 @@ Automatable::get_parameter_automation_state (Evoral::Parameter param)
        AutoState result = Off;
 
        boost::shared_ptr<AutomationControl> c = automation_control(param);
-       
+
        if (c) {
                result = c->automation_state();
        }
@@ -385,16 +395,21 @@ Automatable::transport_stopped (framepos_t now)
                   when the transport is re-started, a touch will magically
                   be happening without it ever have being started in the usual way.
                */
+               const bool list_did_write = !l->in_new_write_pass ();
+
                l->stop_touch (true, now);
-               l->write_pass_finished (now, Config->get_automation_thinning_factor());
 
-               if (l->automation_playback()) {
-                       c->set_value(c->list()->eval(now));
-               }
+               c->commit_transaction (list_did_write);
+
+               l->write_pass_finished (now, Config->get_automation_thinning_factor ());
 
-               if (l->automation_state() == Write) {
+               if (l->automation_state () == Write) {
                        l->set_automation_state (Touch);
                }
+
+               if (l->automation_playback ()) {
+                       c->set_value_unchecked (c->list ()->eval (now));
+               }
        }
 }
 
@@ -435,12 +450,9 @@ Automatable::control_factory(const Evoral::Parameter& param)
                        warning << "PluginPropertyAutomation for non-Plugin" << endl;
                }
        } else if (param.type() == GainAutomation) {
-               Amp* amp = dynamic_cast<Amp*>(this);
-               if (amp) {
-                       control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
-               } else {
-                       warning << "GainAutomation for non-Amp" << endl;
-               }
+               control = new GainControl(_a_session, param);
+       } else if (param.type() == TrimAutomation) {
+               control = new GainControl(_a_session, param);
        } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
                Pannable* pannable = dynamic_cast<Pannable*>(this);
                if (pannable) {
@@ -485,3 +497,42 @@ Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
 {
        return ARDOUR::value_as_string(ac->desc(), ac->get_value());
 }
+
+bool
+Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
+{
+       Controls::const_iterator li;
+
+       next_event.when = std::numeric_limits<double>::max();
+
+       for (li = _controls.begin(); li != _controls.end(); ++li) {
+               boost::shared_ptr<AutomationControl> c
+                       = boost::dynamic_pointer_cast<AutomationControl>(li->second);
+
+               if (only_active && (!c || !c->automation_playback())) {
+                       continue;
+               }
+
+               Evoral::ControlList::const_iterator i;
+               boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
+               Evoral::ControlEvent cp (now, 0.0f);
+               if (!alist) {
+                       continue;
+               }
+
+               for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
+                    i != alist->end() && (*i)->when < end; ++i) {
+                       if ((*i)->when > now) {
+                               break;
+                       }
+               }
+
+               if (i != alist->end() && (*i)->when < end) {
+                       if ((*i)->when < next_event.when) {
+                               next_event.when = (*i)->when;
+                       }
+               }
+       }
+
+       return next_event.when != std::numeric_limits<double>::max();
+}