summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/ardour_ui.cc2
-rw-r--r--gtk2_ardour/ardour_ui_dialogs.cc3
-rw-r--r--gtk2_ardour/automation_line.cc42
-rw-r--r--gtk2_ardour/automation_line.h13
-rw-r--r--gtk2_ardour/automation_time_axis.cc5
-rw-r--r--libs/ardour/ardour/automatable.h4
-rw-r--r--libs/ardour/ardour/automation_control.h31
-rw-r--r--libs/ardour/ardour/debug.h1
-rw-r--r--libs/ardour/ardour/rc_configuration_vars.h2
-rw-r--r--libs/ardour/ardour/route.h2
-rw-r--r--libs/ardour/audio_track.cc2
-rw-r--r--libs/ardour/automatable.cc60
-rw-r--r--libs/ardour/automation_control.cc60
-rw-r--r--libs/ardour/automation_list.cc27
-rw-r--r--libs/ardour/buffer_manager.cc6
-rw-r--r--libs/ardour/debug.cc1
-rw-r--r--libs/ardour/midi_track.cc2
-rw-r--r--libs/ardour/plugin_insert.cc2
-rw-r--r--libs/ardour/route.cc34
-rw-r--r--libs/ardour/session_transport.cc19
-rw-r--r--libs/ardour/track.cc9
-rw-r--r--libs/ardour/wscript1
-rw-r--r--libs/evoral/evoral/ControlList.hpp28
-rw-r--r--libs/evoral/evoral/ControlSet.hpp2
-rw-r--r--libs/evoral/evoral/types.hpp1
-rw-r--r--libs/evoral/src/ControlList.cpp389
-rw-r--r--libs/evoral/src/debug.cpp1
27 files changed, 500 insertions, 249 deletions
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index c5d2bf5c11..9c637761b0 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -62,6 +62,7 @@
#include "ardour/ardour.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
+#include "ardour/automation_watch.h"
#include "ardour/diskstream.h"
#include "ardour/filename_extensions.h"
#include "ardour/port.h"
@@ -878,6 +879,7 @@ ARDOUR_UI::ask_about_saving_session (const vector<string>& actions)
return -1;
}
+
gint
ARDOUR_UI::every_second ()
{
diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc
index 7fc50df673..0116f4e842 100644
--- a/gtk2_ardour/ardour_ui_dialogs.cc
+++ b/gtk2_ardour/ardour_ui_dialogs.cc
@@ -25,6 +25,7 @@
#include "ardour/session.h"
#include "ardour/audioengine.h"
+#include "ardour/automation_watch.h"
#include "actions.h"
#include "add_route_dialog.h"
@@ -77,6 +78,8 @@ ARDOUR_UI::set_session (Session *s)
}
}
+ AutomationWatch::instance().set_session (s);
+
if (location_ui->get()) {
location_ui->get()->set_session(s);
}
diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc
index 4a21e7905a..6586aed966 100644
--- a/gtk2_ardour/automation_line.cc
+++ b/gtk2_ardour/automation_line.cc
@@ -28,6 +28,8 @@
#include "ardour/automation_list.h"
#include "ardour/dB.h"
+#include "ardour/debug.h"
+
#include "evoral/Curve.hpp"
#include "simplerect.h"
@@ -79,6 +81,7 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
_visible = Line;
update_pending = false;
+ have_timeout = false;
_uses_gain_mapping = false;
no_draw = false;
_is_boolean = false;
@@ -124,15 +127,6 @@ AutomationLine::event_handler (GdkEvent* event)
}
void
-AutomationLine::queue_reset ()
-{
- if (!update_pending) {
- update_pending = true;
- Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&AutomationLine::reset, this));
- }
-}
-
-void
AutomationLine::show ()
{
if (_visible & Line) {
@@ -829,7 +823,12 @@ void AutomationLine::set_colors ()
void
AutomationLine::list_changed ()
{
- queue_reset ();
+ DEBUG_TRACE (DEBUG::Automation, string_compose ("\tline changed, existing update pending? %1\n", update_pending));
+
+ if (!update_pending) {
+ update_pending = true;
+ Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&AutomationLine::queue_reset, this));
+ }
}
void
@@ -936,7 +935,9 @@ AutomationLine::reset_callback (const Evoral::ControlList& events)
void
AutomationLine::reset ()
{
+ DEBUG_TRACE (DEBUG::Automation, "\t\tLINE RESET\n");
update_pending = false;
+ have_timeout = false;
if (no_draw) {
return;
@@ -946,6 +947,27 @@ AutomationLine::reset ()
}
void
+AutomationLine::queue_reset ()
+{
+ /* this must be called from the GUI thread
+ */
+
+ if (trackview.editor().session()->transport_rolling() && alist->automation_write()) {
+ /* automation write pass ... defer to a timeout */
+ /* redraw in 1/4 second */
+ if (!have_timeout) {
+ DEBUG_TRACE (DEBUG::Automation, "\tqueue timeout\n");
+ Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &AutomationLine::reset), false), 250);
+ have_timeout = true;
+ } else {
+ DEBUG_TRACE (DEBUG::Automation, "\ttimeout already queued, change ignored\n");
+ }
+ } else {
+ reset ();
+ }
+}
+
+void
AutomationLine::clear ()
{
/* parent must create and commit command */
diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h
index 6be7ccd6b8..73734bcd40 100644
--- a/gtk2_ardour/automation_line.h
+++ b/gtk2_ardour/automation_line.h
@@ -166,12 +166,13 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
bool _our_time_converter;
VisibleAspects _visible;
-
- bool _uses_gain_mapping : 1;
- bool terminal_points_can_slide : 1;
- bool update_pending : 1;
- bool no_draw : 1;
- bool _is_boolean : 1;
+
+ bool _uses_gain_mapping;
+ bool terminal_points_can_slide;
+ bool update_pending;
+ bool have_timeout;
+ bool no_draw;
+ bool _is_boolean;
/** true if we did a push at any point during the current drag */
bool did_push;
diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc
index 7c9f38aa0f..da6b66994c 100644
--- a/gtk2_ardour/automation_time_axis.cc
+++ b/gtk2_ardour/automation_time_axis.cc
@@ -621,6 +621,11 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float t
AutomationSelection::iterator p;
boost::shared_ptr<AutomationList> alist(line.the_list());
+ if (_session->transport_rolling() && alist->automation_write()) {
+ /* do not paste if this control is in write mode and we're rolling */
+ return false;
+ }
+
for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
if (p == selection.lines.end()) {
diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h
index dc86c0cddd..6e0f7a97b5 100644
--- a/libs/ardour/ardour/automatable.h
+++ b/libs/ardour/ardour/automatable.h
@@ -45,7 +45,7 @@ public:
Automatable(Session&);
Automatable (const Automatable& other);
- virtual ~Automatable() {}
+ virtual ~Automatable();
boost::shared_ptr<Evoral::Control>
control_factory(const Evoral::Parameter& id);
@@ -59,7 +59,7 @@ public:
virtual void add_control(boost::shared_ptr<Evoral::Control>);
void clear_controls ();
- virtual void automation_snapshot (framepos_t now, bool force);
+ virtual void transport_located (framepos_t now);
virtual void transport_stopped (framepos_t now);
virtual std::string describe_parameter(Evoral::Parameter param);
diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h
index 2c15a1b1b0..10194b3f9b 100644
--- a/libs/ardour/ardour/automation_control.h
+++ b/libs/ardour/ardour/automation_control.h
@@ -22,6 +22,8 @@
#define __ardour_automation_control_h__
#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
#include "pbd/controllable.h"
#include "evoral/Control.hpp"
#include "ardour/automation_list.h"
@@ -34,7 +36,7 @@ class Automatable;
/** A PBD::Controllable with associated automation data (AutomationList)
*/
-class AutomationControl : public PBD::Controllable, public Evoral::Control
+class AutomationControl : public PBD::Controllable, public Evoral::Control, public boost::enable_shared_from_this<AutomationControl>
{
public:
AutomationControl(ARDOUR::Session&,
@@ -42,37 +44,34 @@ public:
boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(),
const std::string& name="");
+ ~AutomationControl ();
+
boost::shared_ptr<AutomationList> alist() const {
return boost::dynamic_pointer_cast<AutomationList>(_list);
}
- void set_list(boost::shared_ptr<Evoral::ControlList>);
+ void set_list (boost::shared_ptr<Evoral::ControlList>);
inline bool automation_playback() const {
- return ((ARDOUR::AutomationList*)_list.get())->automation_playback();
+ return alist()->automation_playback();
}
inline bool automation_write() const {
- return ((ARDOUR::AutomationList*)_list.get())->automation_write();
+ return alist()->automation_write();
}
inline AutoState automation_state() const {
- return ((ARDOUR::AutomationList*)_list.get())->automation_state();
+ return alist()->automation_state();
}
- inline void set_automation_state(AutoState as) {
- return ((ARDOUR::AutomationList*)_list.get())->set_automation_state(as);
+ inline AutoStyle automation_style() const {
+ return alist()->automation_style();
}
- inline void start_touch(double when) {
- set_touching (true);
- return ((ARDOUR::AutomationList*)_list.get())->start_touch(when);
- }
-
- inline void stop_touch(bool mark, double when) {
- set_touching (false);
- return ((ARDOUR::AutomationList*)_list.get())->stop_touch(mark, when);
- }
+ void set_automation_state(AutoState as);
+ void set_automation_style(AutoStyle as);
+ void start_touch (double when);
+ void stop_touch (bool mark, double when);
void set_value (double);
double get_value () const;
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h
index 5c72c9236f..334aac53e6 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -60,6 +60,7 @@ namespace PBD {
extern uint64_t TempoMath;
extern uint64_t TempoMap;
extern uint64_t OrderKeys;
+ extern uint64_t Automation;
}
}
diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h
index 37599b0ac6..09b0c55a74 100644
--- a/libs/ardour/ardour/rc_configuration_vars.h
+++ b/libs/ardour/ardour/rc_configuration_vars.h
@@ -152,7 +152,7 @@ CONFIG_VARIABLE (int32_t, history_depth, "history-depth", 20)
CONFIG_VARIABLE (bool, use_overlap_equivalency, "use-overlap-equivalency", false)
CONFIG_VARIABLE (bool, periodic_safety_backups, "periodic-safety-backups", true)
CONFIG_VARIABLE (uint32_t, periodic_safety_backup_interval, "periodic-safety-backup-interval", 120)
-CONFIG_VARIABLE (float, automation_interval, "automation-interval", 50)
+CONFIG_VARIABLE (float, automation_interval, "automation-interval", 500)
CONFIG_VARIABLE (bool, sync_all_route_ordering, "sync-all-route-ordering", true)
CONFIG_VARIABLE (bool, only_copy_imported_files, "only-copy-imported-files", false)
CONFIG_VARIABLE (bool, keep_tearoffs, "keep-tearoffs", false)
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h
index f6c737d766..22ecb19123 100644
--- a/libs/ardour/ardour/route.h
+++ b/libs/ardour/ardour/route.h
@@ -131,6 +131,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
virtual void nonrealtime_handle_transport_stopped (bool abort, bool did_locate, bool flush_processors);
virtual void realtime_handle_transport_stopped () {}
virtual void realtime_locate () {}
+ virtual void non_realtime_locate (framepos_t);
virtual void set_pending_declick (int);
/* end of vfunc-based API */
@@ -409,7 +410,6 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
boost::shared_ptr<Processor> the_instrument() const;
InstrumentInfo& instrument_info() { return _instrument_info; }
- void automation_snapshot (framepos_t now, bool force=false);
void protect_automation ();
enum {
diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc
index 6161147f44..4079cdb481 100644
--- a/libs/ardour/audio_track.cc
+++ b/libs/ardour/audio_track.cc
@@ -321,8 +321,6 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
framepos_t transport_frame;
boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
- automation_snapshot (start_frame, false);
-
if (n_outputs().n_total() == 0 && _processors.empty()) {
return 0;
}
diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc
index d0a605bcd9..608ae745de 100644
--- a/libs/ardour/automatable.cc
+++ b/libs/ardour/automatable.cc
@@ -47,14 +47,12 @@ const string Automatable::xml_node_name = X_("Automation");
Automatable::Automatable(Session& session)
: _a_session(session)
- , _last_automation_snapshot(0)
{
}
Automatable::Automatable (const Automatable& other)
: ControlSet (other)
, _a_session (other._a_session)
- , _last_automation_snapshot (0)
{
Glib::Mutex::Lock lm (other._control_lock);
@@ -63,6 +61,18 @@ Automatable::Automatable (const Automatable& other)
add_control (ac);
}
}
+
+Automatable::~Automatable ()
+{
+ {
+ Glib::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)
{
@@ -74,8 +84,6 @@ Automatable::old_set_automation_state (const XMLNode& node)
warning << _("Automation node has no path property") << endmsg;
}
- _last_automation_snapshot = 0;
-
return 0;
}
@@ -102,8 +110,6 @@ Automatable::load_automation (const string& path)
set<Evoral::Parameter> tosave;
controls().clear ();
- _last_automation_snapshot = 0;
-
while (in) {
double when;
double value;
@@ -228,8 +234,6 @@ Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter le
}
}
- _last_automation_snapshot = 0;
-
return 0;
}
@@ -258,11 +262,10 @@ Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState
{
Glib::Mutex::Lock lm (control_lock());
- boost::shared_ptr<Evoral::Control> c = control (param, true);
- boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
+ boost::shared_ptr<AutomationControl> c = automation_control (param, true);
- if (s != l->automation_state()) {
- l->set_automation_state (s);
+ if (c && (s != c->automation_state())) {
+ c->set_automation_state (s);
_a_session.set_dirty ();
}
}
@@ -272,11 +275,10 @@ Automatable::get_parameter_automation_state (Evoral::Parameter param)
{
AutoState result = Off;
- boost::shared_ptr<Evoral::Control> c = control(param);
- boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
-
+ boost::shared_ptr<AutomationControl> c = automation_control(param);
+
if (c) {
- result = l->automation_state();
+ result = c->automation_state();
}
return result;
@@ -287,11 +289,10 @@ Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle
{
Glib::Mutex::Lock lm (control_lock());
- boost::shared_ptr<Evoral::Control> c = control(param, true);
- boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
+ boost::shared_ptr<AutomationControl> c = automation_control(param, true);
- if (s != l->automation_style()) {
- l->set_automation_style (s);
+ if (c && (s != c->automation_style())) {
+ c->set_automation_style (s);
_a_session.set_dirty ();
}
}
@@ -336,19 +337,20 @@ Automatable::protect_automation ()
}
void
-Automatable::automation_snapshot (framepos_t now, bool force)
+Automatable::transport_located (framepos_t now)
{
- if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
+ for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
+
+ boost::shared_ptr<AutomationControl> c
+ = boost::dynamic_pointer_cast<AutomationControl>(li->second);
+ if (c) {
+ boost::shared_ptr<AutomationList> l
+ = boost::dynamic_pointer_cast<AutomationList>(c->list());
- for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
- boost::shared_ptr<AutomationControl> c
- = boost::dynamic_pointer_cast<AutomationControl>(i->second);
- if (_a_session.transport_rolling() && c->automation_write()) {
- c->list()->rt_add (now, i->second->user_double());
+ if (l) {
+ l->start_write_pass (now);
}
}
-
- _last_automation_snapshot = now;
}
}
diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc
index 05463dcdd0..1fde567e28 100644
--- a/libs/ardour/automation_control.cc
+++ b/libs/ardour/automation_control.cc
@@ -21,6 +21,7 @@
#include <iostream>
#include "ardour/automation_control.h"
+#include "ardour/automation_watch.h"
#include "ardour/event_type_map.h"
#include "ardour/session.h"
@@ -39,6 +40,10 @@ AutomationControl::AutomationControl(
{
}
+AutomationControl::~AutomationControl ()
+{
+}
+
/** Get the current effective `user' value based on automation state */
double
AutomationControl::get_value() const
@@ -52,17 +57,18 @@ AutomationControl::get_value() const
* @param value `user' value
*/
void
-AutomationControl::set_value(double value)
+AutomationControl::set_value (double value)
{
- bool to_list = _list && _session.transport_stopped()
- && ((AutomationList*)_list.get())->automation_write();
+ bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
if (to_list && parameter().toggled()) {
// store the previous value just before this so any
// interpolation works right
- _list->add (get_double(), _session.transport_frame()-1);
+ bool erase_since_last = _session.transport_rolling();
+
+ _list->add (get_double(), _session.transport_frame()-1, erase_since_last);
}
Control::set_double (value, to_list, _session.transport_frame());
@@ -72,9 +78,51 @@ AutomationControl::set_value(double value)
void
-AutomationControl::set_list(boost::shared_ptr<Evoral::ControlList> list)
+AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
{
- Control::set_list(list);
+ Control::set_list (list);
Changed(); /* EMIT SIGNAL */
}
+void
+AutomationControl::set_automation_state (AutoState as)
+{
+ if (as != alist()->automation_state()) {
+
+ cerr << name() << " setting automation state to " << enum_2_string (as) << endl;
+
+ if (as == Write) {
+ AutomationWatch::instance().add_automation_watch (shared_from_this());
+ } else if (as == Touch) {
+ if (!touching()) {
+ AutomationWatch::instance().remove_automation_watch (shared_from_this());
+ }
+ } else {
+ AutomationWatch::instance().remove_automation_watch (shared_from_this());
+ }
+
+ alist()->set_automation_state (as);
+ }
+}
+
+void
+AutomationControl::set_automation_style (AutoStyle as)
+{
+ alist()->set_automation_style (as);
+}
+
+void
+AutomationControl::start_touch(double when)
+{
+ set_touching (true);
+ AutomationWatch::instance().add_automation_watch (shared_from_this());
+ alist()->start_touch(when);
+}
+
+void
+AutomationControl::stop_touch(bool mark, double when)
+{
+ set_touching (false);
+ AutomationWatch::instance().remove_automation_watch (shared_from_this());
+ alist()->stop_touch (mark, when);
+}
diff --git a/libs/ardour/automation_list.cc b/libs/ardour/automation_list.cc
index 26ca7b097a..39e7bacc46 100644
--- a/libs/ardour/automation_list.cc
+++ b/libs/ardour/automation_list.cc
@@ -180,11 +180,6 @@ AutomationList::set_automation_state (AutoState s)
{
if (s != _state) {
_state = s;
-
- if (_state == Write) {
- Glib::Mutex::Lock lm (ControlList::_lock);
- nascent.push_back (new NascentInfo ());
- }
automation_state_changed (s); /* EMIT SIGNAL */
}
}
@@ -202,8 +197,7 @@ void
AutomationList::start_touch (double when)
{
if (_state == Touch) {
- Glib::Mutex::Lock lm (ControlList::_lock);
- nascent.push_back (new NascentInfo (when));
+ start_write_pass (when);
}
g_atomic_int_set (&_touching, 1);
@@ -223,22 +217,11 @@ AutomationList::stop_touch (bool mark, double when)
if (_state == Touch) {
- assert (!nascent.empty ());
-
- Glib::Mutex::Lock lm (ControlList::_lock);
-
if (mark) {
-
- nascent.back()->end_time = when;
-
- } else {
-
- /* nascent info created in start touch but never used. just get rid of it.
- */
-
- NascentInfo* ninfo = nascent.back ();
- nascent.erase (nascent.begin());
- delete ninfo;
+
+ /* XXX need to mark the last added point with the
+ * current time
+ */
}
}
}
diff --git a/libs/ardour/buffer_manager.cc b/libs/ardour/buffer_manager.cc
index e3bc2cb97c..71ff4946f3 100644
--- a/libs/ardour/buffer_manager.cc
+++ b/libs/ardour/buffer_manager.cc
@@ -48,7 +48,7 @@ BufferManager::init (uint32_t size)
thread_buffers->write (&ts, 1);
thread_buffers_list->push_back (ts);
}
- cerr << "Initialized thread buffers, readable count now " << thread_buffers->read_space() << endl;
+ // cerr << "Initialized thread buffers, readable count now " << thread_buffers->read_space() << endl;
}
@@ -59,7 +59,7 @@ BufferManager::get_thread_buffers ()
ThreadBuffers* tbp;
if (thread_buffers->read (&tbp, 1) == 1) {
- cerr << "Got thread buffers, readable count now " << thread_buffers->read_space() << endl;
+ // cerr << "Got thread buffers, readable count now " << thread_buffers->read_space() << endl;
return tbp;
}
@@ -71,7 +71,7 @@ BufferManager::put_thread_buffers (ThreadBuffers* tbp)
{
Glib::Mutex::Lock em (rb_mutex);
thread_buffers->write (&tbp, 1);
- cerr << "Put back thread buffers, readable count now " << thread_buffers->read_space() << endl;
+ // cerr << "Put back thread buffers, readable count now " << thread_buffers->read_space() << endl;
}
void
diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc
index db0f409d11..0d0948d576 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -57,5 +57,6 @@ uint64_t PBD::DEBUG::Layering = PBD::new_debug_bit ("layering");
uint64_t PBD::DEBUG::TempoMath = PBD::new_debug_bit ("tempomath");
uint64_t PBD::DEBUG::TempoMap = PBD::new_debug_bit ("tempomap");
uint64_t PBD::DEBUG::OrderKeys = PBD::new_debug_bit ("orderkeys");
+uint64_t PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
index 1d352d622b..800e7949d5 100644
--- a/libs/ardour/midi_track.cc
+++ b/libs/ardour/midi_track.cc
@@ -283,8 +283,6 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
- automation_snapshot (start_frame);
-
if (n_outputs().n_total() == 0 && _processors.empty()) {
return 0;
}
diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc
index 6e7cfe373f..f390e190f5 100644
--- a/libs/ardour/plugin_insert.cc
+++ b/libs/ardour/plugin_insert.cc
@@ -267,7 +267,7 @@ PluginInsert::parameter_changed (uint32_t which, float val)
boost::shared_ptr<AutomationControl> ac = automation_control (Evoral::Parameter (PluginAutomation, 0, which));
if (ac) {
- ac->set_double (val);
+ ac->set_value (val);
Plugins::iterator i = _plugins.begin();
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index 1a6bff5096..80b738e003 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -2930,10 +2930,6 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_lo
{
Glib::RWLock::ReaderLock lm (_processor_lock);
- if (!did_locate) {
- automation_snapshot (now, true);
- }
-
Automatable::transport_stopped (now);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -3061,8 +3057,6 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
return 0;
}
- automation_snapshot (_session.transport_frame(), false);
-
if (n_outputs().n_total() == 0) {
return 0;
}
@@ -3274,18 +3268,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
}
}
-void
-Route::automation_snapshot (framepos_t now, bool force)
-{
- if (_pannable) {
- _pannable->automation_snapshot (now, force);
- }
-
- for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- (*i)->automation_snapshot (now, force);
- }
-}
-
Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
: AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
boost::shared_ptr<AutomationList>(), name)
@@ -4174,3 +4156,19 @@ Route::the_instrument () const
}
return boost::shared_ptr<Processor>();
}
+
+void
+Route::non_realtime_locate (framepos_t pos)
+{
+ if (_pannable) {
+ _pannable->transport_located (pos);
+ }
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->transport_located (pos);
+ }
+ }
+}
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index a878b9fabc..b5fb2a790f 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -299,8 +299,8 @@ Session::butler_transport_work ()
if (tr) {
tr->adjust_playback_buffering ();
/* and refill those buffers ... */
- tr->non_realtime_locate (_transport_frame);
}
+ (*i)->non_realtime_locate (_transport_frame);
}
}
@@ -344,10 +344,8 @@ Session::butler_transport_work ()
if (!(ptw & PostTransportLocate)) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && !tr->hidden()) {
- tr->non_realtime_locate (_transport_frame);
- }
+ (*i)->non_realtime_locate (_transport_frame);
+
if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
/* new request, stop seeking, and start again */
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
@@ -420,10 +418,7 @@ Session::non_realtime_locate ()
{
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr) {
- tr->non_realtime_locate (_transport_frame);
- }
+ (*i)->non_realtime_locate (_transport_frame);
}
/* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
@@ -601,10 +596,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n"));
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", (*i)->name()));
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && !tr->hidden()) {
- tr->non_realtime_locate (_transport_frame);
- }
+ (*i)->non_realtime_locate (_transport_frame);
if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
@@ -1236,7 +1228,6 @@ Session::start_transport ()
if (tr) {
tr->realtime_set_speed (tr->speed(), true);
}
- (*i)->automation_snapshot (_transport_frame, true);
}
if (!_engine.freewheeling()) {
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
index 0dc0a9f676..452a0843e2 100644
--- a/libs/ardour/track.cc
+++ b/libs/ardour/track.cc
@@ -620,7 +620,14 @@ Track::non_realtime_input_change ()
void
Track::non_realtime_locate (framepos_t p)
{
- _diskstream->non_realtime_locate (p);
+ Route::non_realtime_locate (p);
+
+ if (!hidden()) {
+ /* don't waste i/o cycles and butler calls
+ for hidden (secret) tracks
+ */
+ _diskstream->non_realtime_locate (p);
+ }
}
void
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 04f67f7eb1..672dae6abd 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -55,6 +55,7 @@ libardour_sources = [
'automation.cc',
'automation_control.cc',
'automation_list.cc',
+ 'automation_watch.cc',
'beats_frames_converter.cc',
'broadcast_info.cc',
'buffer.cc',
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
index 1e546e04fa..c6127a4e9b 100644
--- a/libs/evoral/evoral/ControlList.hpp
+++ b/libs/evoral/evoral/ControlList.hpp
@@ -124,10 +124,8 @@ public:
virtual bool clamp_value (double& /*when*/, double& /*value*/) const { return true; }
- void rt_add (double when, double value);
- void add (double when, double value);
+ void add (double when, double value, bool erase_since_last_add = false);
void fast_simple_add (double when, double value);
- void merge_nascent (double when);
void erase_range (double start, double end);
void erase (iterator);
@@ -245,6 +243,7 @@ public:
virtual bool touching() const { return false; }
virtual bool writing() const { return false; }
virtual bool touch_enabled() const { return false; }
+ void start_write_pass (double time);
void write_pass_finished (double when);
/** Emitted when mark_dirty() is called on this object */
@@ -257,6 +256,8 @@ public:
bool operator!= (ControlList const &) const;
+ void invalidate_insert_iterator ();
+
protected:
/** Called by unlocked_eval() to handle cases of 3 or more control points. */
@@ -287,21 +288,14 @@ protected:
Curve* _curve;
- struct NascentInfo {
- EventList events;
- double start_time;
- double end_time;
- double same_value_cnt;
-
- NascentInfo (double start = -1.0)
- : start_time (start)
- , end_time (-1.0)
- , same_value_cnt (0)
- {}
- };
-
- std::list<NascentInfo*> nascent;
static double _thinning_factor;
+
+ private:
+ iterator insert_iterator;
+ double insert_position;
+ bool new_write_pass;
+ bool did_write_during_pass;
+ void unlocked_invalidate_insert_iterator ();
};
} // namespace Evoral
diff --git a/libs/evoral/evoral/ControlSet.hpp b/libs/evoral/evoral/ControlSet.hpp
index 293d411755..8df942b31f 100644
--- a/libs/evoral/evoral/ControlSet.hpp
+++ b/libs/evoral/evoral/ControlSet.hpp
@@ -38,7 +38,7 @@ class ControlSet : public boost::noncopyable {
public:
ControlSet();
ControlSet (const ControlSet&);
- virtual ~ControlSet() {}
+ virtual ~ControlSet() {}
virtual boost::shared_ptr<Evoral::Control>
control_factory(const Evoral::Parameter& id) = 0;
diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp
index 35dec6de0b..7bdbdc7a2e 100644
--- a/libs/evoral/evoral/types.hpp
+++ b/libs/evoral/evoral/types.hpp
@@ -52,6 +52,7 @@ namespace PBD {
namespace DEBUG {
extern uint64_t Sequence;
extern uint64_t Note;
+ extern uint64_t ControlList;
}
}
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index 9b672612ed..9ae269453d 100644
--- a/libs/evoral/src/ControlList.cpp
+++ b/libs/evoral/src/ControlList.cpp
@@ -23,7 +23,10 @@
#include "evoral/ControlList.hpp"
#include "evoral/Curve.hpp"
+#include "pbd/compose.h"
+
using namespace std;
+using namespace PBD;
namespace Evoral {
@@ -63,6 +66,10 @@ ControlList::ControlList (const Parameter& id)
_search_cache.left = -1;
_search_cache.first = _events.end();
_sort_pending = false;
+ new_write_pass = true;
+ did_write_during_pass = false;
+ insert_position = -1;
+ insert_iterator = _events.end();
}
ControlList::ControlList (const ControlList& other)
@@ -78,6 +85,10 @@ ControlList::ControlList (const ControlList& other)
_lookup_cache.range.first = _events.end();
_search_cache.first = _events.end();
_sort_pending = false;
+ new_write_pass = true;
+ did_write_during_pass = false;
+ insert_position = -1;
+ insert_iterator = _events.end();
copy_events (other);
@@ -106,6 +117,11 @@ ControlList::ControlList (const ControlList& other, double start, double end)
copy_events (*(section.get()));
}
+ new_write_pass = false;
+ did_write_during_pass = false;
+ insert_position = -1;
+ insert_iterator = _events.end();
+
mark_dirty ();
}
@@ -115,13 +131,6 @@ ControlList::~ControlList()
delete (*x);
}
- for (list<NascentInfo*>::iterator n = nascent.begin(); n != nascent.end(); ++n) {
- for (EventList::iterator x = (*n)->events.begin(); x != (*n)->events.end(); ++x) {
- delete *x;
- }
- delete (*n);
- }
-
delete _curve;
}
@@ -161,6 +170,7 @@ ControlList::copy_events (const ControlList& other)
for (const_iterator i = other.begin(); i != other.end(); ++i) {
_events.push_back (new ControlEvent ((*i)->when, (*i)->value));
}
+ unlocked_invalidate_insert_iterator ();
mark_dirty ();
}
maybe_signal_changed ();
@@ -195,6 +205,7 @@ ControlList::clear ()
{
Glib::Mutex::Lock lm (_lock);
_events.clear ();
+ unlocked_invalidate_insert_iterator ();
mark_dirty ();
}
@@ -233,16 +244,21 @@ ControlList::_x_scale (double factor)
void
ControlList::write_pass_finished (double when)
{
- merge_nascent (when);
+ if (did_write_during_pass) {
+ thin ();
+ }
+ new_write_pass = true;
+ did_write_during_pass = false;
}
-
struct ControlEventTimeComparator {
bool operator() (ControlEvent* a, ControlEvent* b) {
return a->when < b->when;
}
};
+#if 0
+
void
ControlList::merge_nascent (double when)
{
@@ -432,96 +448,75 @@ ControlList::merge_nascent (double when)
maybe_signal_changed ();
}
-
-void
-ControlList::rt_add (double when, double value)
-{
- // this is for automation recording
-
- if (touch_enabled() && !touching()) {
- return;
- }
-
- // cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
-
- Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
-
- if (lm.locked()) {
- assert (!nascent.empty());
- /* we don't worry about adding events out of time order as we will
- sort them in merge_nascent.
- */
-
- NascentInfo* ni (nascent.back());
- EventList& el (ni->events);
-
- if (!el.empty() && (when >= el.back()->when) && (value == el.back()->value)) {
-
- /* same value, later timestamp, effective slope is
- * zero, so just move the last point in nascent to our
- * new time position. this avoids storing an unlimited
- * number of points to represent a flat line.
- */
-
- ni->same_value_cnt++;
-
- if (ni->same_value_cnt > 1) {
- el.back()->when = when;
- return;
- }
- } else {
- ni->same_value_cnt = 0;
- }
-
- el.push_back (new ControlEvent (when, value));
- }
-}
+#endif
void
ControlList::thin ()
{
- Glib::Mutex::Lock lm (_lock);
-
- ControlEvent* prevprev = 0;
- ControlEvent* cur = 0;
- ControlEvent* prev = 0;
- iterator pprev;
- int counter = 0;
+ bool changed = false;
- for (iterator i = _events.begin(); i != _events.end(); ++i) {
-
- cur = *i;
- counter++;
-
- if (counter > 2) {
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ ControlEvent* prevprev = 0;
+ ControlEvent* cur = 0;
+ ControlEvent* prev = 0;
+ iterator pprev;
+ int counter = 0;
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 thin from %2 events\n", this, _events.size()));
+
+ for (iterator i = _events.begin(); i != _events.end(); ++i) {
- double area = fabs ((prevprev->when * (prev->value - cur->value)) +
- (prev->when * (cur->value - prevprev->value)) +
- (cur->when * (prevprev->value - prev->value)));
+ cur = *i;
+ counter++;
- if (area < _thinning_factor) {
- iterator tmp = pprev;
-
- /* pprev will change to current
- i is incremented to the next event
- */
-
- pprev = i;
- _events.erase (tmp);
-
- continue;
+ if (counter > 2) {
+
+ /* compute the area of the triangle formed by 3 points
+ */
+
+ double area = fabs ((prevprev->when * (prev->value - cur->value)) +
+ (prev->when * (cur->value - prevprev->value)) +
+ (cur->when * (prevprev->value - prev->value)));
+
+ if (area < _thinning_factor) {
+ iterator tmp = pprev;
+
+ /* pprev will change to current
+ i is incremented to the next event
+ as we loop.
+ */
+
+ pprev = i;
+ _events.erase (tmp);
+ changed = true;
+ continue;
+ }
}
+
+ prevprev = prev;
+ prev = cur;
+ pprev = i;
}
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 thin => %2 events\n", this, _events.size()));
- prevprev = prev;
- prev = cur;
- pprev = i;
+ if (changed) {
+ unlocked_invalidate_insert_iterator ();
+ mark_dirty ();
+ }
+ }
+
+ if (changed) {
+ maybe_signal_changed ();
}
}
void
ControlList::fast_simple_add (double when, double value)
{
+ Glib::Mutex::Lock lm (_lock);
/* to be used only for loading pre-sorted data from saved state */
_events.insert (_events.end(), new ControlEvent (when, value));
assert(_events.back());
@@ -530,7 +525,41 @@ ControlList::fast_simple_add (double when, double value)
}
void
-ControlList::add (double when, double value)
+ControlList::invalidate_insert_iterator ()
+{
+ Glib::Mutex::Lock lm (_lock);
+ unlocked_invalidate_insert_iterator ();
+}
+
+void
+ControlList::unlocked_invalidate_insert_iterator ()
+{
+ insert_iterator = _events.end();
+}
+
+void
+ControlList::start_write_pass (double when)
+{
+ Glib::Mutex::Lock lm (_lock);
+
+ new_write_pass = true;
+ did_write_during_pass = false;
+ insert_position = when;
+
+ ControlEvent cp (when, 0.0);
+ insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ if ((*insert_iterator)->when != when) {
+ /* doesn't point at a control point at precisely this time,
+ so reset it to the end and we'll find where to insert
+ if/when a new control event is added.
+ */
+ unlocked_invalidate_insert_iterator ();
+ }
+}
+
+void
+ControlList::add (double when, double value, bool erase_since_last_add)
{
/* this is for making changes from some kind of user interface or
control surface (GUI, MIDI, OSC etc)
@@ -540,37 +569,188 @@ ControlList::add (double when, double value)
return;
}
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add %2 at %3 w/erase = %4\n", this, value, when, erase_since_last_add));
+
{
Glib::Mutex::Lock lm (_lock);
ControlEvent cp (when, 0.0f);
- bool insert = true;
iterator insertion_point;
if (_events.empty()) {
+
+ /* as long as the point we're adding is not at zero,
+ * add an "anchor" point there.
+ */
+
if (when > 1) {
_events.insert (_events.end(), new ControlEvent (0, _default_value));
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _default_value));
}
}
- for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) {
+ if (new_write_pass) {
- /* only one point allowed per time point */
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 new write pass, insert pos = %2, iter @ end ? %3\n",
+ this, insert_position, (insert_iterator == _events.end())));
+
+ /* The first addition of a new control event during a
+ * write pass.
+ *
+ * We need to add a new point at insert_position
+ * corresponding the value there.
+ */
- if ((*insertion_point)->when == when) {
- (*insertion_point)->value = value;
- insert = false;
- break;
+ if (insert_iterator == _events.end()) {
+ /* the insert_iterator is not set, figure out where
+ * it needs to be.
+ */
+
+ ControlEvent cp (insert_position, 0.0);
+ insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 looked up insert iterator for new write pass\n", this));
}
- if ((*insertion_point)->when >= when) {
- break;
+ double eval_value = unlocked_eval (insert_position);
+
+ if (insert_iterator == _events.end()) {
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert iterator at end, adding eval-value there %2\n", this, eval_value));
+
+ _events.push_back (new ControlEvent (insert_position, eval_value));
+ /* leave insert iterator at the end */
+
+ } else if ((*insert_iterator)->when == when) {
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert iterator at existing point, setting eval-value there %2\n", this, eval_value));
+
+ /* insert_iterator points to a control event
+ already at the insert position, so there is
+ nothing to do.
+
+ ... except ...
+
+ advance insert_iterator so that the "real"
+ insert occurs in the right place, since it
+ points to the control event just inserted.
+ */
+
+ ++insert_iterator;
+ } else {
+
+ /* insert a new control event at the right spot
+ */
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert eval-value %2 at iterator\n", this, eval_value));
+
+ insert_iterator = _events.insert (insert_iterator, new ControlEvent (insert_position, eval_value));
+
+ /* advance insert_iterator so that the "real"
+ * insert occurs in the right place, since it
+ * points to the control event just inserted.
+ */
+
+ ++insert_iterator;
}
- }
- if (insert) {
- _events.insert (insertion_point, new ControlEvent (when, value));
+ /* don't do this again till the next write pass */
+
+ new_write_pass = false;
+ did_write_during_pass = true;
+
+ } else if (insert_iterator == _events.end() || when > (*insert_iterator)->when) {
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 need to discover insert iterator (@end ? %2)\n",
+ this, (insert_iterator == _events.end())));
+
+ /* this means that we either *know* we want to insert
+ * at the end, or that we don't know where to insert.
+ *
+ * so ... lets perform some quick checks before we
+ * go doing binary search to figure out where to
+ * insert.
+ */
+
+ if (_events.back()->when == when) {
+
+ /* we need to modify the final point, so
+ make insert_iterator point to it.
+ */
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 modify final value\n", this));
+
+ insert_iterator = _events.end();
+ --insert_iterator;
+
+ } else if (_events.back()->when < when) {
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 plan to append to list\n", this));
+
+ if (erase_since_last_add) {
+ /* remove the final point, because
+ we're adding one beyond it.
+ */
+ delete _events.back();
+ _events.pop_back();
+ }
+
+ /* leaving this here will force an append */
+
+ insert_iterator = _events.end();
+
+ } else {
+
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 find based on lower bound, erase = %2\n", this, erase_since_last_add));
+
+ /* the new point is somewhere within the list,
+ * so figure out where to insert
+ */
+
+ ControlEvent cp (when, 0.0);
+ insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ while (insert_iterator != _events.end()) {
+ if ((*insert_iterator)->when < when) {
+ if (erase_since_last_add) {
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 erase existing @ %2\n", this, (*insert_iterator)));
+ delete *insert_iterator;
+ insert_iterator = _events.erase (insert_iterator);
+ continue;
+ }
+ } else if ((*insert_iterator)->when >= when) {
+ break;
+ }
+ ++insert_iterator;
+ }
+ }
+ }
+
+ /* OK, now we're really ready to add a new point
+ */
+
+ if (insert_iterator == _events.end()) {
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 appending new point at end\n", this));
+ _events.push_back (new ControlEvent (when, value));
+ /* leave insert_iterator as it was: at the end */
+
+ } else if ((*insert_iterator)->when == when) {
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 reset existing point to new value %2\n", this, value));
+ /* only one point allowed per time point, so just
+ * reset the value here.
+ */
+ (*insert_iterator)->value = value;
+ /* insert iterator now points past the control event we just
+ * modified. the next insert needs to be after this,
+ * so..
+ */
+ ++insert_iterator;
+ } else {
+ DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert new point at %2 at iterator at %3\n", this, when, (*insert_iterator)->when));
+ _events.insert (insert_iterator, new ControlEvent (when, value));
+ /* leave insert iterator where it was, since it points
+ * to the next control event AFTER the one we just inserted.
+ */
}
+
mark_dirty ();
}
@@ -582,6 +762,9 @@ ControlList::erase (iterator i)
{
{
Glib::Mutex::Lock lm (_lock);
+ if (insert_iterator == i) {
+ unlocked_invalidate_insert_iterator ();
+ }
_events.erase (i);
mark_dirty ();
}
@@ -594,6 +777,7 @@ ControlList::erase (iterator start, iterator end)
{
Glib::Mutex::Lock lm (_lock);
_events.erase (start, end);
+ unlocked_invalidate_insert_iterator ();
mark_dirty ();
}
maybe_signal_changed ();
@@ -613,6 +797,9 @@ ControlList::erase (double when, double value)
if (i != end ()) {
_events.erase (i);
+ if (insert_iterator == i) {
+ unlocked_invalidate_insert_iterator ();
+ }
}
mark_dirty ();
@@ -654,6 +841,7 @@ ControlList::erase_range_internal (double start, double endt, EventList & events
e = upper_bound (events.begin(), events.end(), &cp, time_comparator);
events.erase (s, e);
if (s != e) {
+ unlocked_invalidate_insert_iterator ();
erased = true;
}
}
@@ -720,6 +908,7 @@ ControlList::modify (iterator iter, double when, double val)
if (!_frozen) {
_events.sort (event_time_less_than);
+ unlocked_invalidate_insert_iterator ();
} else {
_sort_pending = true;
}
@@ -783,6 +972,7 @@ ControlList::thaw ()
if (_sort_pending) {
_events.sort (event_time_less_than);
+ unlocked_invalidate_insert_iterator ();
_sort_pending = false;
}
}
@@ -896,7 +1086,8 @@ ControlList::truncate_end (double last_coordinate)
_events.back()->when = last_coordinate;
_events.back()->value = last_val;
}
-
+
+ unlocked_invalidate_insert_iterator ();
mark_dirty();
}
@@ -996,6 +1187,7 @@ ControlList::truncate_start (double overall_length)
_events.push_front (new ControlEvent (0, first_legal_value));
}
+ unlocked_invalidate_insert_iterator ();
mark_dirty();
}
@@ -1437,6 +1629,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
}
}
+ unlocked_invalidate_insert_iterator ();
mark_dirty ();
}
@@ -1506,6 +1699,7 @@ ControlList::paste (ControlList& alist, double pos, float /*times*/)
}
}
+ unlocked_invalidate_insert_iterator ();
mark_dirty ();
}
@@ -1562,6 +1756,7 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
if (!_frozen) {
_events.sort (event_time_less_than);
+ unlocked_invalidate_insert_iterator ();
} else {
_sort_pending = true;
}
diff --git a/libs/evoral/src/debug.cpp b/libs/evoral/src/debug.cpp
index 28f30d0be3..9e82b93d1b 100644
--- a/libs/evoral/src/debug.cpp
+++ b/libs/evoral/src/debug.cpp
@@ -2,4 +2,5 @@
uint64_t PBD::DEBUG::Sequence = PBD::new_debug_bit ("sequence");
uint64_t PBD::DEBUG::Note = PBD::new_debug_bit ("note");
+uint64_t PBD::DEBUG::ControlList = PBD::new_debug_bit ("controllist");