*/
#include <iomanip>
+
#include "pbd/error.h"
+
#include "ardour/automation_list.h"
#include "ardour/automation_control.h"
#include "ardour/event_type_map.h"
#include "ardour/automatable.h"
#include "ardour/panner.h"
+#include "ardour/session.h"
+
#include "ardour_ui.h"
#include "utils.h"
#include "automation_controller.h"
void
AutomationController::start_touch()
{
- _controllable->start_touch();
+ _controllable->start_touch (_controllable->session().transport_frame());
}
void
-AutomationController::end_touch()
+AutomationController::end_touch ()
{
- _controllable->stop_touch();
+ if (_controllable->automation_state() == Touch) {
+
+ bool mark = false;
+ double when = 0;
+
+ if (_controllable->session().transport_rolling()) {
+ mark = true;
+ when = _controllable->session().transport_frame();
+ }
+
+ _controllable->stop_touch (mark, when);
+ }
}
void
gint
GainMeterBase::start_gain_touch (GdkEventButton*)
{
- _amp->gain_control()->start_touch ();
+ _amp->gain_control()->start_touch (_amp->session().transport_frame());
return FALSE;
}
gint
GainMeterBase::end_gain_touch (GdkEventButton*)
{
- _amp->gain_control()->stop_touch ();
+ _amp->gain_control()->stop_touch (false, _amp->session().transport_frame());
return FALSE;
}
#ifdef HAVE_SLV2
#include "ardour/lv2_plugin.h"
#endif
+#include "ardour/session.h"
#include <lrdf.h>
void
GenericPluginUI::start_touch (GenericPluginUI::ControlUI* cui)
{
- cui->control->start_touch ();
+ cui->control->start_touch (cui->control->session().transport_frame());
}
void
GenericPluginUI::stop_touch (GenericPluginUI::ControlUI* cui)
{
- cui->control->stop_touch ();
+ cui->control->stop_touch (false, cui->control->session().transport_frame());
}
void
boost::shared_ptr<AutomationControl> ac = _panner->pan_control (asz);
if (asz) {
- bc->StartGesture.connect (sigc::mem_fun (*ac, &AutomationControl::start_touch));
- bc->StopGesture.connect (sigc::mem_fun (*ac, &AutomationControl::stop_touch));
+ bc->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
+ boost::weak_ptr<AutomationControl> (ac)));
+ bc->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
+ boost::weak_ptr<AutomationControl>(ac)));
}
char buf[64];
}
}
+void
+PannerUI::start_touch (boost::weak_ptr<AutomationControl> wac)
+{
+ boost::shared_ptr<AutomationControl> ac = wac.lock();
+ if (!ac) {
+ return;
+ }
+ ac->start_touch (ac->session().transport_frame());
+}
+
+void
+PannerUI::stop_touch (boost::weak_ptr<AutomationControl> wac)
+{
+ boost::shared_ptr<AutomationControl> ac = wac.lock();
+ if (!ac) {
+ return;
+ }
+ ac->stop_touch (false, ac->session().transport_frame());
+}
+
bool
PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
{
class Session;
class Panner;
class Delivery;
+ class AutomationControl;
}
+
namespace Gtkmm2ext {
class FastMeter;
}
std::string astyle_string (ARDOUR::AutoStyle);
std::string short_astyle_string (ARDOUR::AutoStyle);
std::string _astyle_string (ARDOUR::AutoStyle, bool);
+
+ void start_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
+ void stop_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
};
#endif /* __ardour_gtk_panner_ui_h__ */
}
void
-Amp::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
{
if (!_active && !_pending_active) {
return;
bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
bool configure_io (ChanCount in, ChanCount out);
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
bool apply_gain() const { return _apply_gain; }
void apply_gain(bool yn) { _apply_gain = yn; }
void clear_controls ();
virtual void automation_snapshot(nframes_t now, bool force);
- virtual void transport_stopped (sframes_t now);
+ virtual void transport_stopped (framepos_t now);
virtual std::string describe_parameter(Evoral::Parameter param);
return ((ARDOUR::AutomationList*)_list.get())->set_automation_state(as);
}
- inline void start_touch() {
+ inline void start_touch(double when) {
set_touching (true);
- return ((ARDOUR::AutomationList*)_list.get())->start_touch();
+ return ((ARDOUR::AutomationList*)_list.get())->start_touch(when);
}
- inline void stop_touch() {
+ inline void stop_touch(bool mark, double when) {
set_touching (false);
- return ((ARDOUR::AutomationList*)_list.get())->stop_touch();
+ return ((ARDOUR::AutomationList*)_list.get())->stop_touch(mark, when);
}
/** Set the value and do the right thing based on automation state
double lower() const { return parameter().min(); }
double upper() const { return parameter().max(); }
+ const ARDOUR::Session& session() const { return _session; }
+
protected:
ARDOUR::Session& _session;
};
PBD::Signal0<void> automation_style_changed;
bool automation_playback() const {
- return (_state & Play) || ((_state & Touch) && !_touching);
+ return (_state & Play) || ((_state & Touch) && !touching());
}
bool automation_write () const {
- return (_state & Write) || ((_state & Touch) && _touching);
- }
+ return ((_state & Write) || ((_state & Touch) && touching()));
+ }
PBD::Signal0<void> StateChanged;
static PBD::Signal1<void,AutomationList*> AutomationListCreated;
- void start_touch ();
- void stop_touch ();
- bool touching() const { return _touching; }
+ void start_touch (double when);
+ void stop_touch (bool mark, double when);
+ bool touching() const { return g_atomic_int_get (&_touching); }
+ bool writing() const { return _state == Write; }
+ bool touch_enabled() const { return _state == Touch; }
XMLNode& get_state ();
int set_state (const XMLNode &, int version);
void maybe_signal_changed ();
- AutoState _state;
- AutoStyle _style;
- bool _touching;
+ AutoState _state;
+ AutoStyle _style;
+ gint _touching;
};
} // namespace
/* supplemental method used with MIDI */
void flush_buffers (nframes_t nframes, nframes64_t time);
- void transport_stopped ();
-
void no_outs_cuz_we_no_monitor(bool);
-
void cycle_start (nframes_t);
void increment_output_offset (nframes_t);
void transport_stopped (sframes_t frame);
void allow_pan_reset ();
uint32_t pans_required() const { return _configured_input.n_audio(); }
- void start_pan_touch (uint32_t which);
- void end_pan_touch (uint32_t which);
+ void start_pan_touch (uint32_t which, double when);
+ void end_pan_touch (uint32_t which, bool mark, double when);
protected:
Role _role;
XMLNode& get_state(void);
int set_state(const XMLNode&, int version);
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
bool configure_io (ChanCount in, ChanCount out);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
int set_block_size (nframes_t);
XMLNode& get_state(void);
int set_state(const XMLNode& node, int version);
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
bool feeds (boost::shared_ptr<Route> other) const;
bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
bool configure_io (ChanCount in, ChanCount out);
void reflect_inputs (const ChanCount& in);
/** Compute peaks */
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
ChanCount input_streams () const { return current_meters; }
ChanCount output_streams () const { return current_meters; }
bool display_to_user() const;
- void run (BufferSet& /*bufs*/, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t /*nframes*/, bool /*result_required*/);
+ void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t /*nframes*/, bool /*result_required*/);
XMLNode& state (bool full);
int set_state (const XMLNode&, int /* version */);
bool can_support_io_configuration (const ChanCount& /*in*/, ChanCount& /*out*/) const { return true; };
/// The fundamental Panner function
- void run (BufferSet& src, BufferSet& dest, sframes_t start_frame, sframes_t end_frames, nframes_t nframes);
+ void run (BufferSet& src, BufferSet& dest, framepos_t start_frame, framepos_t end_frames, nframes_t nframes);
bool bypassed() const { return _bypassed; }
void set_bypassed (bool yn);
XMLNode& get_state(void);
int set_state(const XMLNode&, int version);
- void run (BufferSet& in, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& in, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
void silence (nframes_t nframes);
void activate ();
XMLNode& get_state(void);
int set_state (const XMLNode&, int version);
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
nframes_t signal_latency() const;
virtual nframes_t signal_latency() const { return 0; }
- virtual void transport_stopped (sframes_t /*frame*/) {}
-
virtual int set_block_size (nframes_t /*nframes*/) { return 0; }
virtual bool requires_fixed_sized_buffers() const { return false; }
/** @param result_required true if, on return from this method, bufs is required to contain valid data;
* if false, the method need not bother writing to bufs if it doesn't want to.
*/
- virtual void run (BufferSet& /*bufs*/, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t /*nframes*/, bool result_required) {}
+ virtual void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t /*nframes*/, bool result_required) {}
virtual void silence (nframes_t /*nframes*/) {}
virtual void activate () { _pending_active = true; ActiveChanged(); }
uint32_t bit_slot() const { return _bitslot; }
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
boost::shared_ptr<Amp> amp() const { return _amp; }
boost::shared_ptr<PeakMeter> meter() const { return _meter; }
uint32_t pans_required() const { return _configured_input.n_audio(); }
- void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+ void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
bool configure_io (ChanCount in, ChanCount out);
#include "pbd/error.h"
#include "pbd/enumwriter.h"
+#include "pbd/stacktrace.h"
#include "midi++/names.h"
for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
boost::shared_ptr<AutomationControl> c
= boost::dynamic_pointer_cast<AutomationControl>(i->second);
- if (c->automation_write()) {
+ if (_a_session.transport_rolling() && c->automation_write()) {
c->list()->rt_add (now, i->second->user_double());
}
}
}
void
-Automatable::transport_stopped (sframes_t now)
+Automatable::transport_stopped (framepos_t now)
{
for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
boost::shared_ptr<AutomationControl> c
= boost::dynamic_pointer_cast<AutomationControl>(li->second);
- boost::shared_ptr<AutomationList> l
+ if (c) {
+ boost::shared_ptr<AutomationList> l
= boost::dynamic_pointer_cast<AutomationList>(c->list());
-
- c->list()->reposition_for_rt_add (now);
-
- if (c->automation_state() != Off) {
- c->set_value(c->list()->eval(now));
- }
+
+ if (l) {
+ l->write_pass_finished (now);
+
+ if (l->automation_playback()) {
+ c->set_value(c->list()->eval(now));
+ }
+
+ if (l->automation_state() == Write) {
+ l->set_automation_state (Touch);
+ }
+ }
+ }
}
}
{
_state = Off;
_style = Absolute;
- _touching = false;
+ g_atomic_int_set (&_touching, 0);
create_curve_if_necessary();
{
_style = other._style;
_state = other._state;
- _touching = other._touching;
+ g_atomic_int_set (&_touching, other.touching());
create_curve_if_necessary();
{
_style = other._style;
_state = other._state;
- _touching = other._touching;
+ g_atomic_int_set (&_touching, other.touching());
create_curve_if_necessary();
AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
: ControlList(id)
{
- _touching = false;
+ g_atomic_int_set (&_touching, 0);
_state = Off;
_style = Absolute;
{
if (s != _state) {
_state = s;
+
+ if (_state == Write) {
+ Glib::Mutex::Lock lm (ControlList::_lock);
+ nascent.push_back (new NascentInfo (false));
+ }
+
automation_state_changed (s); /* EMIT SIGNAL */
}
}
}
void
-AutomationList::start_touch ()
+AutomationList::start_touch (double when)
{
- _touching = true;
- _new_value = true;
+ if (_state == Touch) {
+ Glib::Mutex::Lock lm (ControlList::_lock);
+ nascent.push_back (new NascentInfo (true, when));
+ }
+
+ g_atomic_int_set (&_touching, 1);
}
void
-AutomationList::stop_touch ()
+AutomationList::stop_touch (bool mark, double when)
{
- _touching = false;
- _new_value = false;
+ g_atomic_int_set (&_touching, 0);
+
+ if (_state == Touch) {
+ 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;
+ }
+ }
}
void
root->add_property ("interpolation-style", enum_2_string (_interpolation));
if (full) {
- root->add_property ("state", auto_state_to_string (_state));
+ /* never serialize state with Write enabled - too dangerous
+ for the user's data
+ */
+ if (_state != Write) {
+ root->add_property ("state", auto_state_to_string (_state));
+ } else {
+ root->add_property ("state", auto_state_to_string (Off));
+ }
} else {
/* never save anything but Off for automation state to a template */
root->add_property ("state", auto_state_to_string (Off));
error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
} else {
mark_dirty ();
- reposition_for_rt_add (0);
maybe_signal_changed ();
}
if ((prop = node.property (X_("state"))) != 0) {
_state = string_to_auto_state (prop->value());
+ if (_state == Write) {
+ _state = Off;
+ }
} else {
_state = Off;
}
freeze ();
clear ();
mark_dirty ();
- reposition_for_rt_add (0);
maybe_signal_changed ();
thaw ();
}
}
void
-Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool result_required)
+Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool result_required)
{
assert (_output);
void
-Delivery::start_pan_touch (uint32_t which)
+Delivery::start_pan_touch (uint32_t which, double when)
{
if (which < _panner->npanners()) {
- _panner->pan_control(which)->start_touch();
+ _panner->pan_control(which)->start_touch(when);
}
}
void
-Delivery::end_pan_touch (uint32_t which)
+Delivery::end_pan_touch (uint32_t which, bool mark, double when)
{
if (which < _panner->npanners()) {
- _panner->pan_control(which)->stop_touch();
+ _panner->pan_control(which)->stop_touch(mark, when);
}
}
-void
-Delivery::transport_stopped (sframes_t frame)
-{
- _panner->transport_stopped (frame);
-}
void
Delivery::flush_buffers (nframes_t nframes, nframes64_t time)
}
void
-Delivery::transport_stopped ()
+Delivery::transport_stopped (framepos_t now)
{
- /* turn off any notes that are on */
+ Processor::transport_stopped (now);
- PortSet& ports (_output->ports());
+ _panner->transport_stopped (now);
- for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) {
- (*i).transport_stopped ();
- }
+ if (_output) {
+ PortSet& ports (_output->ports());
+
+ for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) {
+ (*i).transport_stopped ();
+ }
+ }
}
gain_t
}
void
-InternalReturn::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+InternalReturn::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
{
if (!_active && !_pending_active) {
return;
}
void
-InternalSend::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+InternalSend::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
{
if ((!_active && !_pending_active) || !target || !_send_to) {
_meter->reset ();
* be set to 0.
*/
void
-PeakMeter::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+PeakMeter::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
{
if (!_active && !_pending_active) {
return;
void
MidiTrack::handle_transport_stopped (bool abort, bool did_locate, bool flush_processors)
{
-
- _main_outs->transport_stopped ();
Route::handle_transport_stopped (abort, did_locate, flush_processors);
}
-
void
MidiTrack::push_midi_input_to_step_edit_ringbuffer (nframes_t nframes)
{
}
void
-MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
+MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
{
uint32_t chn = 0;
gain_t target_gain;
}
void
-Panner::run (BufferSet& inbufs, BufferSet& outbufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes)
+Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
{
if (outbufs.count().n_audio() == 0) {
// Failing to deliver audio we were asked to deliver is a bug
}
void
-PluginInsert::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
{
if (_active || _pending_active) {
}
void
-PortInsert::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+PortInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
{
if (_output->n_ports().n_total() == 0) {
return;
}
void
-Return::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+Return::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
{
if ((!_active && !_pending_active) || _input->n_ports() == ChanCount::ZERO) {
return;
void
Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
{
- nframes_t now = _session.transport_frame();
+ framepos_t now = _session.transport_frame();
{
Glib::RWLock::ReaderLock lm (_processor_lock);
automation_snapshot (now, true);
}
+ Automatable::transport_stopped (now);
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
}
void
-Send::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
{
if (_output->n_ports() == ChanCount::ZERO) {
_meter->reset ();
bool extend_to (double);
void slide (iterator before, double distance);
- void reposition_for_rt_add (double when);
void rt_add (double when, double value);
void add (double when, double value);
void fast_simple_add (double when, double value);
+ void merge_nascent (double when);
void reset_range (double start, double end);
void erase_range (double start, double end);
boost::shared_ptr<ControlList> copy (double, double);
void clear (double, double);
- boost::shared_ptr<ControlList> cut (iterator, iterator);
- boost::shared_ptr<ControlList> copy (iterator, iterator);
- void clear (iterator, iterator);
-
bool paste (ControlList&, double position, float times);
void set_yrange (double min, double max) {
InterpolationStyle interpolation() const { return _interpolation; }
void set_interpolation (InterpolationStyle);
+ virtual bool touching() const { return false; }
+ virtual bool writing() const { return false; }
+ virtual bool touch_enabled() const { return false; }
+ void write_pass_finished (double when);
+
/** Emitted when mark_dirty() is called on this object */
mutable PBD::Signal0<void> Dirty;
/** Emitted when our interpolation style changes */
void _x_scale (double factor);
- mutable LookupCache _lookup_cache;
- mutable SearchCache _search_cache;
-
- Parameter _parameter;
- InterpolationStyle _interpolation;
- EventList _events;
- mutable Glib::Mutex _lock;
- int8_t _frozen;
- bool _changed_when_thawed;
- bool _new_value;
- double _max_xval;
- double _min_yval;
- double _max_yval;
- double _default_value;
- bool _sort_pending;
- iterator _rt_insertion_point;
- double _rt_pos;
+ mutable LookupCache _lookup_cache;
+ mutable SearchCache _search_cache;
+
+ Parameter _parameter;
+ InterpolationStyle _interpolation;
+ EventList _events;
+ mutable Glib::Mutex _lock;
+ int8_t _frozen;
+ bool _changed_when_thawed;
+ double _max_xval;
+ double _min_yval;
+ double _max_yval;
+ double _default_value;
+ bool _sort_pending;
Curve* _curve;
+
+ struct NascentInfo {
+ EventList events;
+ bool is_touch;
+ double start_time;
+ double end_time;
+
+ NascentInfo (bool touching, double start = -1.0)
+ : is_touch (touching)
+ , start_time (start)
+ , end_time (-1.0)
+ {}
+ };
+
+ std::list<NascentInfo*> nascent;
};
} // namespace Evoral
_max_yval = id.max();
_max_xval = 0; // means "no limit"
_default_value = 0;
- _rt_insertion_point = _events.end();
_lookup_cache.left = -1;
_lookup_cache.range.first = _events.end();
_search_cache.left = -1;
_max_yval = other._max_yval;
_max_xval = other._max_xval;
_default_value = other._default_value;
- _rt_insertion_point = _events.end();
_lookup_cache.range.first = _events.end();
_search_cache.first = _events.end();
_sort_pending = false;
_max_yval = other._max_yval;
_max_xval = other._max_xval;
_default_value = other._default_value;
- _rt_insertion_point = _events.end();
_lookup_cache.range.first = _events.end();
_search_cache.first = _events.end();
_sort_pending = false;
for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
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;
}
return true;
}
-void ControlList::_x_scale (double factor)
+void
+ControlList::_x_scale (double factor)
{
for (iterator i = _events.begin(); i != _events.end(); ++i) {
(*i)->when = floor ((*i)->when * factor);
}
void
-ControlList::reposition_for_rt_add (double /*when*/)
+ControlList::write_pass_finished (double when)
{
- _rt_insertion_point = _events.end();
+ merge_nascent (when);
}
void
-ControlList::rt_add (double when, double value)
+ControlList::merge_nascent (double when)
{
- //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
-
- {
- Glib::Mutex::Lock lm (_lock);
-
- iterator where;
- ControlEvent cp (when, 0.0);
- bool done = false;
-
- if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) {
-
- /* we have a previous insertion point, so we should delete
- everything between it and the position where we are going
- to insert this point.
- */
-
- iterator after = _rt_insertion_point;
-
- if (++after != _events.end()) {
- iterator far = after;
-
- while (far != _events.end()) {
- if ((*far)->when > when) {
- break;
- }
- ++far;
- }
-
- if (_new_value) {
- where = far;
- _rt_insertion_point = where;
-
- if ((*where)->when == when) {
- (*where)->value = value;
- done = true;
- }
- } else {
- where = _events.erase (after, far);
- }
-
- } else {
-
- where = after;
-
- }
-
- iterator previous = _rt_insertion_point;
- --previous;
-
- if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) {
- (*_rt_insertion_point)->when = when;
- done = true;
-
- }
-
- } else {
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ if (nascent.empty()) {
+ return;
+ }
+
+ for (list<NascentInfo*>::iterator n = nascent.begin(); n != nascent.end(); ++n) {
+
+ NascentInfo* ninfo = *n;
+ EventList& nascent_events (ninfo->events);
+ bool need_adjacent_start_clamp;
+ bool need_adjacent_end_clamp;
+
+ if (nascent_events.empty()) {
+ delete ninfo;
+ continue;
+ }
+
+ if (ninfo->start_time < 0.0) {
+ ninfo->start_time = nascent_events.front()->when;
+ }
+
+ if (ninfo->end_time < 0.0) {
+ ninfo->end_time = when;
+ }
+
+ bool preexisting = !_events.empty();
+
+ if (!preexisting) {
+
+ _events = nascent_events;
+
+ } else if (ninfo->end_time < _events.front()->when) {
+
+ /* all points in nascent are before the first existing point */
+
+ _events.insert (_events.begin(), nascent_events.begin(), nascent_events.end());
+
+ } else if (ninfo->start_time > _events.back()->when) {
+
+ /* all points in nascent are after the last existing point */
+
+ _events.insert (_events.end(), nascent_events.begin(), nascent_events.end());
+
+ } else {
+
+ /* find the range that overaps with nascent events,
+ and insert the contents of nascent events.
+ */
+
+ iterator i;
+ iterator range_begin = _events.end();
+ iterator range_end = _events.end();
+ double end_value = unlocked_eval (ninfo->end_time);
+ double start_value = unlocked_eval (ninfo->start_time - 1);
+
+ need_adjacent_end_clamp = true;
+ need_adjacent_start_clamp = true;
+
+ for (i = _events.begin(); i != _events.end(); ++i) {
+
+ if ((*i)->when == ninfo->start_time) {
+ /* existing point at same time, remove it
+ and the consider the next point instead.
+ */
+ i = _events.erase (i);
+
+ if (i == _events.end()) {
+ break;
+ }
+
+ if (range_begin == _events.end()) {
+ range_begin = i;
+ need_adjacent_start_clamp = false;
+ } else {
+ need_adjacent_end_clamp = false;
+ }
+
+ if ((*i)->when > ninfo->end_time) {
+ range_end = i;
+ break;
+ }
+
+ } else if ((*i)->when > ninfo->start_time) {
+
+ if (range_begin == _events.end()) {
+ range_begin = i;
+ }
+
+ if ((*i)->when > ninfo->end_time) {
+ range_end = i;
+ break;
+ }
+ }
+ }
+
+ assert (range_begin != _events.end());
+
+ if (range_begin != _events.begin()) {
+ /* clamp point before */
+ if (need_adjacent_start_clamp) {
+ _events.insert (range_begin, new ControlEvent (ninfo->start_time, start_value));
+ }
+ }
+
+ _events.insert (range_begin, nascent_events.begin(), nascent_events.end());
+
+ if (range_end != _events.end()) {
+ /* clamp point after */
+ if (need_adjacent_end_clamp) {
+ _events.insert (range_begin, new ControlEvent (ninfo->end_time, end_value));
+ }
+ }
+
+ _events.erase (range_begin, range_end);
+ }
+
+ delete ninfo;
+ }
+
+ nascent.clear ();
+
+ if (writing()) {
+ nascent.push_back (new NascentInfo (false));
+ }
+ }
- where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+ maybe_signal_changed ();
+}
- if (where != _events.end()) {
- if ((*where)->when == when) {
- (*where)->value = value;
- done = true;
- }
- }
- }
+void
+ControlList::rt_add (double when, double value)
+{
+ // this is for automation recording
+
+ if (touch_enabled() && !touching()) {
+ return;
+ }
- if (!done) {
- _rt_insertion_point = _events.insert (where, new ControlEvent (when, value));
- }
+ //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
- _new_value = false;
- mark_dirty ();
- }
+ Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
- maybe_signal_changed ();
+ if (lm.locked()) {
+ assert (!nascent.empty());
+ if (!nascent.back()->events.empty()) {
+ assert (when > nascent.back()->events.back()->when);
+ }
+ nascent.back()->events.push_back (new ControlEvent (when, value));
+ }
}
void
if (insert) {
_events.insert (insertion_point, new ControlEvent (when, value));
- reposition_for_rt_add (0);
}
{
Glib::Mutex::Lock lm (_lock);
_events.erase (i);
- reposition_for_rt_add (0);
mark_dirty ();
}
maybe_signal_changed ();
{
Glib::Mutex::Lock lm (_lock);
_events.erase (start, end);
- reposition_for_rt_add (0);
mark_dirty ();
}
maybe_signal_changed ();
erased = erase_range_internal (start, endt, _events);
if (erased) {
- reposition_for_rt_add (0);
mark_dirty ();
}
_events.back()->value = last_val;
}
- reposition_for_rt_add (0);
mark_dirty();
}
_events.push_front (new ControlEvent (0, first_legal_value));
}
- reposition_for_rt_add (0);
-
mark_dirty();
}
}
}
-boost::shared_ptr<ControlList>
-ControlList::cut (iterator start, iterator end)
-{
- boost::shared_ptr<ControlList> nal = create (_parameter);
-
- {
- Glib::Mutex::Lock lm (_lock);
-
- for (iterator x = start; x != end; ) {
- iterator tmp;
-
- tmp = x;
- ++tmp;
-
- nal->_events.push_back (new ControlEvent (**x));
- _events.erase (x);
-
- reposition_for_rt_add (0);
-
- x = tmp;
- }
-
- mark_dirty ();
- }
-
- maybe_signal_changed ();
-
- return nal;
-}
/** @param start Start position in model coordinates.
* @param end End position in model coordinates.
ControlList::cut_copy_clear (double start, double end, int op)
{
boost::shared_ptr<ControlList> nal = create (_parameter);
-
iterator s, e;
- bool changed = false;
+ ControlEvent cp (start, 0.0);
{
- Glib::Mutex::Lock lm (_lock);
+ Glib::Mutex::Lock lm (_lock);
+
+ /* first, determine s & e, two iterators that define the range of points
+ affected by this operation
+ */
- /* find the first event in our list that is at or before `start' in time */
- ControlEvent cp (start, 0.0);
if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) == _events.end()) {
return nal;
}
cp.when = end;
e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
- if (op != 2 && (*s)->when != start) {
- nal->_events.push_back (new ControlEvent (0, unlocked_eval (start)));
- }
- for (iterator x = s; x != e; ) {
- iterator tmp = x;
- ++tmp;
+ /* if "start" isn't the location of an existing point,
+ evaluate the curve to get a value for the start. Add a point to
+ both the existing event list, and if its not a "clear" operation,
+ to the copy ("nal") as well.
+
+ Note that the time positions of the points in each list are different
+ because we want the copy ("nal") to have a zero time reference.
+ */
+
+
+ /* before we begin any cut/clear operations, get the value of the curve
+ at "end".
+ */
- changed = true;
+ double end_value = unlocked_eval (end);
+
+ if ((*s)->when != start) {
+
+ double val = unlocked_eval (start);
+
+ if (op == 0) { // cut
+ if (start > _events.front()->when) {
+ _events.insert (s, (new ControlEvent (start, val)));
+ }
+ }
+
+ if (op != 2) { // ! clear
+ nal->_events.push_back (new ControlEvent (0, val));
+ }
+ }
+
+ for (iterator x = s; x != e; ) {
/* adjust new points to be relative to start, which
has been set to zero.
*/
-
+
if (op != 2) {
nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value));
}
if (op != 1) {
- _events.erase (x);
- }
-
- x = tmp;
+ x = _events.erase (x);
+ } else {
+ ++x;
+ }
}
+
+ if (e == _events.end() || (*e)->when != end) {
- if (op != 2 && nal->_events.back()->when != end - start) {
- nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end)));
- }
+ /* only add a boundary point if there is a point after "end"
+ */
+
+ if (op == 0 && (e != _events.end() && end < (*e)->when)) { // cut
+ _events.insert (e, new ControlEvent (end, end_value));
+ }
- if (changed) {
- reposition_for_rt_add (0);
+ if (op != 2 && (e != _events.end() && end < (*e)->when)) { // cut/copy
+ nal->_events.push_back (new ControlEvent (end - start, end_value));
+ }
}
- mark_dirty ();
+ mark_dirty ();
}
- maybe_signal_changed ();
+ if (op != 1) {
+ maybe_signal_changed ();
+ }
return nal;
-
}
-boost::shared_ptr<ControlList>
-ControlList::copy (iterator start, iterator end)
-{
- boost::shared_ptr<ControlList> nal = create (_parameter);
-
- {
- Glib::Mutex::Lock lm (_lock);
-
- for (iterator x = start; x != end; ) {
- iterator tmp;
-
- tmp = x;
- ++tmp;
-
- nal->_events.push_back (new ControlEvent (**x));
-
- x = tmp;
- }
- }
-
- return nal;
-}
boost::shared_ptr<ControlList>
ControlList::cut (double start, double end)
}
}
- reposition_for_rt_add (0);
mark_dirty ();
}
_sort_pending = true;
}
- reposition_for_rt_add (0);
mark_dirty ();
}