mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +01:00
dramatic overhaul of automation. too long to explain here. this work is not finished - write/touch passes do not correctly overwrite existing data because the semantics of ControlList::insert_iterator need clarification. more to follow
git-svn-id: svn://localhost/ardour2/branches/3.0@13038 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
b04cd7d704
commit
0532e2063b
27 changed files with 500 additions and 249 deletions
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -123,15 +126,6 @@ AutomationLine::event_handler (GdkEvent* event)
|
|||
return PublicEditor::instance().canvas_line_event (event, line, this);
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
|
|
@ -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;
|
||||
|
|
@ -945,6 +946,27 @@ AutomationLine::reset ()
|
|||
alist->apply_to_points (*this, &AutomationLine::reset_callback);
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ namespace PBD {
|
|||
extern uint64_t TempoMath;
|
||||
extern uint64_t TempoMap;
|
||||
extern uint64_t OrderKeys;
|
||||
extern uint64_t Automation;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
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());
|
||||
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());
|
||||
|
||||
if (l) {
|
||||
l->start_write_pass (now);
|
||||
}
|
||||
}
|
||||
|
||||
_last_automation_snapshot = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace PBD {
|
|||
namespace DEBUG {
|
||||
extern uint64_t Sequence;
|
||||
extern uint64_t Note;
|
||||
extern uint64_t ControlList;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
bool changed = false;
|
||||
|
||||
ControlEvent* prevprev = 0;
|
||||
ControlEvent* cur = 0;
|
||||
ControlEvent* prev = 0;
|
||||
iterator pprev;
|
||||
int counter = 0;
|
||||
|
||||
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,36 +569,187 @@ 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;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
}
|
||||
|
||||
if (insert) {
|
||||
_events.insert (insertion_point, new ControlEvent (when, value));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue