diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/automatable.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/automation_control.h | 31 | ||||
-rw-r--r-- | libs/ardour/ardour/debug.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/rc_configuration_vars.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/route.h | 2 | ||||
-rw-r--r-- | libs/ardour/audio_track.cc | 2 | ||||
-rw-r--r-- | libs/ardour/automatable.cc | 60 | ||||
-rw-r--r-- | libs/ardour/automation_control.cc | 60 | ||||
-rw-r--r-- | libs/ardour/automation_list.cc | 27 | ||||
-rw-r--r-- | libs/ardour/buffer_manager.cc | 6 | ||||
-rw-r--r-- | libs/ardour/debug.cc | 1 | ||||
-rw-r--r-- | libs/ardour/midi_track.cc | 2 | ||||
-rw-r--r-- | libs/ardour/plugin_insert.cc | 2 | ||||
-rw-r--r-- | libs/ardour/route.cc | 34 | ||||
-rw-r--r-- | libs/ardour/session_transport.cc | 19 | ||||
-rw-r--r-- | libs/ardour/track.cc | 9 | ||||
-rw-r--r-- | libs/ardour/wscript | 1 | ||||
-rw-r--r-- | libs/evoral/evoral/ControlList.hpp | 28 | ||||
-rw-r--r-- | libs/evoral/evoral/ControlSet.hpp | 2 | ||||
-rw-r--r-- | libs/evoral/evoral/types.hpp | 1 | ||||
-rw-r--r-- | libs/evoral/src/ControlList.cpp | 389 | ||||
-rw-r--r-- | libs/evoral/src/debug.cpp | 1 |
22 files changed, 451 insertions, 233 deletions
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"); |