*/
-#include <fstream>
#include <cstdio>
#include <errno.h>
+#include "pbd/gstdio_compat.h"
#include <glibmm/miscutils.h>
#include "pbd/error.h"
#include "ardour/amp.h"
#include "ardour/automatable.h"
#include "ardour/event_type_map.h"
+#include "ardour/gain_control.h"
+#include "ardour/monitor_control.h"
#include "ardour/midi_track.h"
#include "ardour/pan_controllable.h"
#include "ardour/pannable.h"
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
+#include "ardour/record_enable_control.h"
#include "ardour/session.h"
#include "ardour/uri_map.h"
#include "ardour/value_as_string.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
{
{
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 ();
}
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());
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)")
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? */
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;
}
boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
- if (al) {
+ boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
+
+ if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
al->automation_state_changed.connect_same_thread (
_list_connections,
boost::bind (&Automatable::automation_list_automation_state_changed,
ControlSet::add_control (ac);
- if (al) {
+ if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
_can_automate_list.insert (param);
automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
}
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) {
return string_compose("Bender [%1]", int(param.channel()) + 1);
} else if (param.type() == MidiChannelPressureAutomation) {
return string_compose("Pressure [%1]", int(param.channel()) + 1);
+ } else if (param.type() == MidiNotePressureAutomation) {
+ return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
#ifdef LV2_SUPPORT
} else if (param.type() == PluginPropertyAutomation) {
return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
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())
if (param.type() == NullAutomation) {
warning << "Automation has null type" << endl;
continue;
- }
+ }
+
+ if (_can_automate_list.find (param) == _can_automate_list.end ()) {
+ warning << "Ignored automation data for non-automatable parameter" << endl;
+ continue;
+ }
if (!id_prop) {
warning << "AutomationList node without automation-id property, "
if (c && (s != c->automation_state())) {
c->set_automation_state (s);
_a_session.set_dirty ();
+ AutomationStateChanged(); /* Emit signal */
}
}
AutoState result = Off;
boost::shared_ptr<AutomationControl> c = automation_control(param);
-
+
if (c) {
result = c->automation_state();
}
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));
+ }
}
}
bool make_list = true;
ParameterDescriptor desc(param);
boost::shared_ptr<AutomationList> list;
+
if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
if (mt) {
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) {
} else {
warning << "PanAutomation for non-Pannable" << endl;
}
+ } else if (param.type() == RecEnableAutomation) {
+ Recordable* re = dynamic_cast<Recordable*> (this);
+ if (re) {
+ control = new RecordEnableControl (_a_session, X_("recenable"), *re);
+ }
+ } else if (param.type() == MonitoringAutomation) {
+ Monitorable* m = dynamic_cast<Monitorable*>(this);
+ if (m) {
+ control = new MonitorControl (_a_session, X_("monitor"), *m);
+ }
+ } else if (param.type() == SoloAutomation) {
+ Soloable* s = dynamic_cast<Soloable*>(this);
+ Muteable* m = dynamic_cast<Muteable*>(this);
+ if (s && m) {
+ control = new SoloControl (_a_session, X_("solo"), *s, *m);
+ }
+ } else if (param.type() == MuteAutomation) {
+ Muteable* m = dynamic_cast<Muteable*>(this);
+ if (m) {
+ control = new MuteControl (_a_session, X_("mute"), *m);
+ }
}
if (make_list && !list) {
}
string
-Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
+Automatable::value_as_string (boost::shared_ptr<const 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();
+}