along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id$
*/
#ifndef __ardour_automation_event_h__
#include <sigc++/signal.h>
#include <glibmm/thread.h>
+#include <boost/pool/pool.hpp>
+#include <boost/pool/pool_alloc.hpp>
+
#include <pbd/undo.h>
#include <pbd/xml++.h>
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
+#include <ardour/parameter.h>
namespace ARDOUR {
-
+
+class Curve;
+
struct ControlEvent {
- double when;
- double value;
-
+
ControlEvent (double w, double v)
- : when (w), value (v) { }
+ : when (w), value (v), coeff (0) {
+ }
+
ControlEvent (const ControlEvent& other)
- : when (other.when), value (other.value) {}
+ : when (other.when), value (other.value), coeff (0) {
+ if (other.coeff) {
+ create_coeffs();
+ for (size_t i=0; i < 4; ++i)
+ coeff[i] = other.coeff[i];
+ }
+ }
- virtual ~ControlEvent() {}
-
-// bool operator==(const ControlEvent& other) {
-// return value == other.value && when == other.when;
-// }
+ ~ControlEvent() { if (coeff) delete[] coeff; }
+
+ void create_coeffs() {
+ if (!coeff)
+ coeff = new double[4];
+
+ coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
+ }
+ double when;
+ double value;
+ double* coeff; ///< double[4] allocated by Curve as needed
};
+/* automation lists use a pool allocator that does not use a lock and
+ allocates 8k of new pointers at a time
+*/
+
+typedef boost::fast_pool_allocator<ControlEvent*,
+ boost::default_user_allocator_new_delete,
+ boost::details::pool::null_mutex,
+ 8192> ControlEventAllocator;
+
class AutomationList : public PBD::StatefulDestructible
{
public:
- typedef std::list<ControlEvent*> AutomationEventList;
- typedef AutomationEventList::iterator iterator;
- typedef AutomationEventList::const_iterator const_iterator;
+ typedef std::list<ControlEvent*,ControlEventAllocator> EventList;
+ typedef EventList::iterator iterator;
+ typedef EventList::reverse_iterator reverse_iterator;
+ typedef EventList::const_iterator const_iterator;
- AutomationList (double default_value);
- AutomationList (const XMLNode&);
+ AutomationList (Parameter id, double min_val, double max_val, double default_val);
+ AutomationList (const XMLNode&, Parameter id);
~AutomationList();
AutomationList (const AutomationList&);
AutomationList& operator= (const AutomationList&);
bool operator== (const AutomationList&);
+ const Parameter& parameter() const { return _parameter; }
+ void set_parameter(Parameter p) { _parameter = p; }
+
void freeze();
void thaw ();
- AutomationEventList::size_type size() const { return events.size(); }
- bool empty() const { return events.empty(); }
+ EventList::size_type size() const { return _events.size(); }
+ bool empty() const { return _events.empty(); }
void reset_default (double val) {
- default_value = val;
+ _default_value = val;
}
void clear ();
void x_scale (double factor);
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);
- /* this should be private but old-school automation loading needs it in IO/Redirect */
+ /* this should be private but old-school automation loading needs it in IO/IOProcessor */
void fast_simple_add (double when, double value);
void reset_range (double start, double end);
sigc::signal<void> automation_style_changed;
void set_automation_style (AutoStyle m);
- AutoStyle automation_style() const { return _style; }
+ AutoStyle automation_style() const { return _style; }
sigc::signal<void> automation_state_changed;
- bool automation_playback() {
+ bool automation_playback() const {
return (_state & Play) || ((_state & Touch) && !_touching);
}
- bool automation_write () {
+ bool automation_write () const {
return (_state & Write) || ((_state & Touch) && _touching);
}
bool touching() const { return _touching; }
void set_yrange (double min, double max) {
- min_yval = min;
- max_yval = max;
+ _min_yval = min;
+ _max_yval = max;
}
- double get_max_y() const { return max_yval; }
- double get_min_y() const { return min_yval; }
+ double get_max_y() const { return _max_yval; }
+ double get_min_y() const { return _min_yval; }
void truncate_end (double length);
void truncate_start (double length);
- iterator begin() { return events.begin(); }
- iterator end() { return events.end(); }
+ iterator begin() { return _events.begin(); }
+ iterator end() { return _events.end(); }
- ControlEvent* back() { return events.back(); }
- ControlEvent* front() { return events.front(); }
+ ControlEvent* back() { return _events.back(); }
+ ControlEvent* front() { return _events.front(); }
- const_iterator const_begin() const { return events.begin(); }
- const_iterator const_end() const { return events.end(); }
+ const_iterator const_begin() const { return _events.begin(); }
+ const_iterator const_end() const { return _events.end(); }
std::pair<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when);
template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) {
- Glib::Mutex::Lock lm (lock);
+ Glib::Mutex::Lock lm (_lock);
(obj.*method)(*this);
}
-
- sigc::signal<void,Change> StateChanged;
+
+ sigc::signal<void> StateChanged;
XMLNode& get_state(void);
int set_state (const XMLNode &s);
XMLNode& serialize_events ();
void set_max_xval (double);
- double get_max_xval() const { return max_xval; }
+ double get_max_xval() const { return _max_xval; }
double eval (double where) {
- Glib::Mutex::Lock lm (lock);
+ Glib::Mutex::Lock lm (_lock);
return unlocked_eval (where);
}
double rt_safe_eval (double where, bool& ok) {
- Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK);
+ Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
if ((ok = lm.locked())) {
return unlocked_eval (where);
}
}
- struct TimeComparator {
- bool operator() (const ControlEvent* a, const ControlEvent* b) {
- return a->when < b->when;
- }
- };
-
- static sigc::signal<void, AutomationList*> AutomationListCreated;
-
- protected:
-
- AutomationEventList events;
- mutable Glib::Mutex lock;
- bool _frozen;
- bool changed_when_thawed;
- bool _dirty;
-
+ static inline bool time_comparator (const ControlEvent* a, const ControlEvent* b) {
+ return a->when < b->when;
+ }
+
+ /** Lookup cache for eval functions, range contains equivalent values */
struct LookupCache {
+ LookupCache() : left(-1) {}
double left; /* leftmost x coordinate used when finding "range" */
- std::pair<AutomationList::iterator,AutomationList::iterator> range;
+ std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
+ };
+
+ /** Lookup cache for point finding, range contains points between left and right */
+ struct SearchCache {
+ SearchCache() : left(-1), right(-1) {}
+ double left; /* leftmost x coordinate used when finding "range" */
+ double right; /* rightmost x coordinate used when finding "range" */
+ std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
};
- LookupCache lookup_cache;
+ static sigc::signal<void, AutomationList*> AutomationListCreated;
- AutoState _state;
- AutoStyle _style;
- bool _touching;
- bool _new_touch;
- double max_xval;
- double min_yval;
- double max_yval;
- double default_value;
+ const EventList& events() const { return _events; }
+ double default_value() const { return _default_value; }
- iterator rt_insertion_point;
- double rt_pos;
+ // teeny const violations for Curve
+ mutable sigc::signal<void> Dirty;
+ Glib::Mutex& lock() const { return _lock; }
+ LookupCache& lookup_cache() const { return _lookup_cache; }
+ SearchCache& search_cache() const { return _search_cache; }
- void maybe_signal_changed ();
- void mark_dirty ();
- void _x_scale (double factor);
-
- /* called by type-specific unlocked_eval() to handle
- common case of 0, 1 or 2 control points.
- */
+ /** Called by locked entry point and various private
+ * locations where we already hold the lock.
+ *
+ * FIXME: Should this be private? Curve needs it..
+ */
+ double unlocked_eval (double x) const;
+
+ bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
+ bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
- double shared_eval (double x);
+ Curve& curve() { return *_curve; }
+ const Curve& curve() const { return *_curve; }
- /* called by shared_eval() to handle any case of
- 3 or more control points.
- */
+ enum InterpolationStyle {
+ Discrete,
+ Linear,
+ Curved
+ };
- virtual double multipoint_eval (double x);
+ InterpolationStyle interpolation() const { return _interpolation; }
+ void set_interpolation(InterpolationStyle style) { _interpolation = style; }
- /* called by locked entry point and various private
- locations where we already hold the lock.
- */
+ private:
- virtual double unlocked_eval (double where);
+ /** Called by unlocked_eval() to handle cases of 3 or more control points.
+ */
+ double multipoint_eval (double x) const;
- virtual ControlEvent* point_factory (double,double) const;
- virtual ControlEvent* point_factory (const ControlEvent&) const;
+ void build_search_cache_if_necessary(double start, double end) const;
+
+ bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
+ bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
AutomationList* cut_copy_clear (double, double, int op);
int deserialize_events (const XMLNode&);
+
+ void maybe_signal_changed ();
+ void mark_dirty ();
+ 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;
+ AutoState _state;
+ AutoStyle _style;
+ bool _touching;
+ bool _new_touch;
+ double _max_xval;
+ double _min_yval;
+ double _max_yval;
+ double _default_value;
+ bool _sort_pending;
+ iterator _rt_insertion_point;
+ double _rt_pos;
+
+ Curve* _curve;
};
} // namespace