From d357eca668044badcb4bab318e2e74cfffa9a0b0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 19 Sep 2008 00:47:49 +0000 Subject: Factor out sequencing related things into an independant new library: "evoral". Anything related to the storage of events/values over a range of time lives in evoral. This includes MidiModel (Evoral::Sequence) and automation data (AutomationList (Evoral::ControlList), Automatable (Evoral::ControlSet), etc). libs/evoral synced with http://svn.drobilla.net/lad/trunk/evoral r1511. git-svn-id: svn://localhost/ardour2/branches/3.0@3754 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/SConscript | 2 +- libs/ardour/ardour/automatable.h | 34 +- libs/ardour/ardour/automation_control.h | 40 +- libs/ardour/ardour/automation_event.h | 238 +----- libs/ardour/ardour/midi_buffer.h | 14 +- libs/ardour/ardour/midi_model.h | 161 +--- libs/ardour/ardour/midi_region.h | 4 +- libs/ardour/ardour/midi_ring_buffer.h | 11 +- libs/ardour/ardour/midi_source.h | 2 +- libs/ardour/ardour/midi_track.h | 2 +- libs/ardour/ardour/note.h | 82 -- libs/ardour/ardour/panner.h | 10 +- libs/ardour/ardour/parameter.h | 156 ++-- libs/ardour/ardour/plugin_insert.h | 2 +- libs/ardour/ardour/smf_source.h | 4 +- libs/ardour/audio_track.cc | 6 +- libs/ardour/audioregion.cc | 48 +- libs/ardour/automatable.cc | 201 ++--- libs/ardour/automation_control.cc | 41 +- libs/ardour/automation_event.cc | 1264 +------------------------------ libs/ardour/crossfade.cc | 12 +- libs/ardour/gain.cc | 2 +- libs/ardour/import.cc | 2 +- libs/ardour/io.cc | 21 +- libs/ardour/jack_midi_port.cc | 2 +- libs/ardour/meter.cc | 2 +- libs/ardour/midi_buffer.cc | 14 +- libs/ardour/midi_diskstream.cc | 2 +- libs/ardour/midi_model.cc | 689 +---------------- libs/ardour/midi_source.cc | 2 +- libs/ardour/midi_stretch.cc | 2 +- libs/ardour/midi_track.cc | 4 +- libs/ardour/note.cc | 110 --- libs/ardour/panner.cc | 22 +- libs/ardour/parameter.cc | 8 +- libs/ardour/plugin_insert.cc | 41 +- libs/ardour/route.cc | 4 +- libs/ardour/smf_source.cc | 6 +- 38 files changed, 364 insertions(+), 2903 deletions(-) delete mode 100644 libs/ardour/ardour/note.h delete mode 100644 libs/ardour/note.cc (limited to 'libs/ardour') diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 4bdbf7d88a..b51e81c2ca 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -109,7 +109,6 @@ mix.cc mtc_slave.cc midi_clock_slave.cc named_selection.cc -note.cc onset_detector.cc panner.cc parameter.cc @@ -330,6 +329,7 @@ ardour.Merge ([ libraries['glibmm2'], libraries['lrdf'], libraries['midi++2'], + libraries['evoral'], libraries['pbd'], libraries['raptor'], libraries['samplerate'], diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index a2c1d98ae7..ce31721802 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -27,49 +27,33 @@ #include #include #include +#include namespace ARDOUR { class Session; class AutomationControl; -class Automatable : public SessionObject +class Automatable : public SessionObject, virtual public Evoral::ControlSet { public: Automatable(Session&, const std::string& name); virtual ~Automatable() {} - // shorthand for gain, pan, etc - inline boost::shared_ptr - control(AutomationType type, bool create_if_missing=false) { - return control(Parameter(type), create_if_missing); - } - - virtual boost::shared_ptr control(Parameter id, bool create_if_missing=false); - virtual boost::shared_ptr control(Parameter id) const; + boost::shared_ptr control_factory(boost::shared_ptr list) const; + boost::shared_ptr control_list_factory(const Evoral::Parameter& param) const; - boost::shared_ptr control_factory(boost::shared_ptr list); + virtual void add_control(boost::shared_ptr); - typedef std::map > Controls; - Controls& controls() { return _controls; } - const Controls& controls() const { return _controls; } - - virtual void add_control(boost::shared_ptr); - virtual void automation_snapshot(nframes_t now, bool force); bool should_snapshot (nframes_t now) { return (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval); } virtual void transport_stopped(nframes_t now); - virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const; - virtual string describe_parameter(Parameter param); - virtual float default_parameter_value(Parameter param) { return 1.0f; } - virtual void clear_automation(); - AutoState get_parameter_automation_state (Parameter param, bool lock = true); virtual void set_parameter_automation_state (Parameter param, AutoState); @@ -78,14 +62,11 @@ public: void protect_automation (); - void what_has_automation(std::set&) const; - void what_has_visible_automation(std::set&) const; + void what_has_visible_data(std::set&) const; const std::set& what_can_be_automated() const { return _can_automate_list; } void mark_automation_visible(Parameter, bool); - Glib::Mutex& automation_lock() const { return _automation_lock; } - static void set_automation_interval (jack_nframes_t frames) { _automation_interval = frames; } @@ -106,9 +87,6 @@ protected: int load_automation (const std::string& path); int old_set_automation_state(const XMLNode&); - mutable Glib::Mutex _automation_lock; - - Controls _controls; std::set _visible_controls; std::set _can_automate_list; diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 68ac5797dc..c414f7bc40 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include namespace ARDOUR { @@ -34,28 +36,42 @@ class Automatable; /** A PBD:Controllable with associated automation data (AutomationList) */ -class AutomationControl : public PBD::Controllable +class AutomationControl : public PBD::Controllable, public Evoral::Control { public: AutomationControl(ARDOUR::Session&, boost::shared_ptr, std::string name="unnamed controllable"); + + boost::shared_ptr alist() { return boost::dynamic_pointer_cast(_list); } + + void set_list(boost::shared_ptr); + + inline bool automation_playback() const { + return ((ARDOUR::AutomationList*)_list.get())->automation_playback(); + } + + inline bool automation_write() const { + return ((ARDOUR::AutomationList*)_list.get())->automation_write(); + } + + inline AutoState automation_state() { + return ((ARDOUR::AutomationList*)_list.get())->automation_state(); + } + + inline void start_touch() { + return ((ARDOUR::AutomationList*)_list.get())->start_touch(); + } + + inline void stop_touch() { + return ((ARDOUR::AutomationList*)_list.get())->stop_touch(); + } void set_value(float val); float get_value() const; - float user_value() const; - - void set_list(boost::shared_ptr); - - boost::shared_ptr list() { return _list; } - boost::shared_ptr list() const { return _list; } - - Parameter parameter() const; protected: - ARDOUR::Session& _session; - boost::shared_ptr _list; - float _user_value; + ARDOUR::Session& _session; }; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index 4362b9c867..ed3379bc15 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -37,105 +37,29 @@ #include #include -namespace ARDOUR { - -class Curve; - -struct ControlEvent { - - ControlEvent (double w, double v) - : when (w), value (v), coeff (0) { - } +#include - ControlEvent (const ControlEvent& other) - : 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]; - } - } - - ~ControlEvent() { if (coeff) delete[] coeff; } +using Evoral::ControlEvent; - 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 ControlEventAllocator; +namespace ARDOUR { -class AutomationList : public PBD::StatefulDestructible +class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlList { public: - typedef std::list EventList; - typedef EventList::iterator iterator; - typedef EventList::reverse_iterator reverse_iterator; - typedef EventList::const_iterator const_iterator; - - AutomationList (Parameter id, double min_val, double max_val, double default_val); + AutomationList (Parameter id); AutomationList (const XMLNode&, Parameter id); ~AutomationList(); + virtual boost::shared_ptr create(Evoral::Parameter id); + AutomationList (const AutomationList&); AutomationList (const AutomationList&, double start, double end); 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 (); - - EventList::size_type size() const { return _events.size(); } - bool empty() const { return _events.empty(); } - - void reset_default (double 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/IOProcessor */ - void fast_simple_add (double when, double value); - - void reset_range (double start, double end); - void erase_range (double start, double end); - void erase (iterator); - void erase (iterator, iterator); - void move_range (iterator start, iterator end, double, double); - void modify (iterator, double, double); - - AutomationList* cut (double, double); - AutomationList* copy (double, double); - void clear (double, double); - - AutomationList* cut (iterator, iterator); - AutomationList* copy (iterator, iterator); - void clear (iterator, iterator); - - bool paste (AutomationList&, double position, float times); + void mark_dirty () const; void set_automation_state (AutoState); AutoState automation_state() const { return _state; } @@ -151,157 +75,29 @@ class AutomationList : public PBD::StatefulDestructible bool automation_write () const { return (_state & Write) || ((_state & Touch) && _touching); } + + sigc::signal StateChanged; + + static sigc::signal AutomationListCreated; + mutable sigc::signal Dirty; void start_touch (); void stop_touch (); bool touching() const { return _touching; } - void set_yrange (double min, double max) { - _min_yval = min; - _max_yval = max; - } - - 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(); } - - 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(); } - - std::pair control_points_adjacent (double when); - - template void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) { - Glib::Mutex::Lock lm (_lock); - (obj.*method)(*this); - } - - sigc::signal StateChanged; - XMLNode& get_state(void); int set_state (const XMLNode &s); XMLNode& state (bool full); XMLNode& serialize_events (); - void set_max_xval (double); - double get_max_xval() const { return _max_xval; } - - double eval (double where) { - 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); - - if ((ok = lm.locked())) { - return unlocked_eval (where); - } else { - return 0.0; - } - } - - 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 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 range; - }; - - static sigc::signal AutomationListCreated; - - const EventList& events() const { return _events; } - double default_value() const { return _default_value; } - - // teeny const violations for Curve - mutable sigc::signal Dirty; - Glib::Mutex& lock() const { return _lock; } - LookupCache& lookup_cache() const { return _lookup_cache; } - SearchCache& search_cache() const { return _search_cache; } - - /** 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; - - Curve& curve() { return *_curve; } - const Curve& curve() const { return *_curve; } - - enum InterpolationStyle { - Discrete, - Linear, - Curved - }; - - InterpolationStyle interpolation() const { return _interpolation; } - void set_interpolation(InterpolationStyle style) { _interpolation = style; } - private: - - /** Called by unlocked_eval() to handle cases of 3 or more control points. - */ - double multipoint_eval (double x) 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; + AutoState _state; + AutoStyle _style; + bool _touching; }; } // namespace diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 699f461b17..0c13199825 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -39,7 +39,7 @@ public: void copy(const MidiBuffer& copy); - bool push_back(const MIDI::Event& event); + bool push_back(const Evoral::Event& event); bool push_back(const jack_midi_event_t& event); uint8_t* reserve(double time, size_t size); @@ -50,7 +50,7 @@ public: struct iterator { iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline MIDI::Event& operator*() const { return buffer[index]; } + inline Evoral::Event& operator*() const { return buffer[index]; } inline iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const iterator& other) const { return index != other.index; } @@ -61,7 +61,7 @@ public: struct const_iterator { const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline const MIDI::Event& operator*() const { return buffer[index]; } + inline const Evoral::Event& operator*() const { return buffer[index]; } inline const_iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const const_iterator& other) const { return index != other.index; } @@ -80,8 +80,8 @@ private: friend class iterator; friend class const_iterator; - const MIDI::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; } - MIDI::Event& operator[](size_t i) { assert(i < _size); return _events[i]; } + const Evoral::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; } + Evoral::Event& operator[](size_t i) { assert(i < _size); return _events[i]; } // FIXME: Eliminate this static const size_t MAX_EVENT_SIZE = 4; // bytes @@ -92,8 +92,8 @@ private: /* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */ - MIDI::Event* _events; ///< Event structs that point to offsets in _data - uint8_t* _data; ///< MIDI, straight up. No time stamps. + Evoral::Event* _events; ///< Event structs that point to offsets in _data + uint8_t* _data; ///< MIDI, straight up. No time stamps. }; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 2481aa8d34..51bddfde44 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -31,84 +31,28 @@ #include #include #include -#include #include +#include +#include namespace ARDOUR { class Session; class MidiSource; -/** - * This class keeps track of the current x and y for a control - */ -class MidiControlIterator { -public: - boost::shared_ptr automation_list; - double x; - double y; - - MidiControlIterator(boost::shared_ptr a_list, - double a_x, - double a_y) - : automation_list(a_list) - , x(a_x) - , y(a_y) - {} -}; - - /** This is a higher level (than MidiBuffer) model of MIDI data, with separate * representations for notes (instead of just unassociated note on/off events) * and controller data. Controller data is represented as part of the * Automatable base (i.e. in a map of AutomationList, keyed by Parameter). + * Because of this MIDI controllers and automatable controllers/widgets/etc + * are easily interchangeable. */ -class MidiModel : public boost::noncopyable, public Automatable { +class MidiModel : public Automatable, public Evoral::Sequence { public: MidiModel(MidiSource* s, size_t size=0); - void write_lock(); - void write_unlock(); - - void read_lock() const; - void read_unlock() const; - - void clear(); - - NoteMode note_mode() const { return _note_mode; } - void set_note_mode(NoteMode mode) { _note_mode = mode; } - - void start_write(); - bool writing() const { return _writing; } - void end_write(bool delete_stuck=false); - - size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; - - /** Resizes vector if necessary (NOT realtime safe) */ - void append(const MIDI::Event& ev); - - inline const boost::shared_ptr note_at(unsigned i) const { return _notes[i]; } - inline const boost::shared_ptr note_at(unsigned i) { return _notes[i]; } - - inline size_t n_notes() const { return _notes.size(); } - inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; } - - inline static bool note_time_comparator (const boost::shared_ptr a, - const boost::shared_ptr b) { - return a->time() < b->time(); - } - - struct LaterNoteEndComparator { - typedef const Note* value_type; - inline bool operator()(const boost::shared_ptr a, - const boost::shared_ptr b) const { - return a->end_time() > b->end_time(); - } - }; - - typedef std::vector< boost::shared_ptr > Notes; - inline Notes& notes() { return _notes; } - inline const Notes& notes() const { return _notes; } + NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } + void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; /** Add/Remove notes. * Technically all operations can be implemented as one of these. @@ -127,17 +71,17 @@ public: int set_state (const XMLNode&); XMLNode& get_state (); - void add(const boost::shared_ptr note); - void remove(const boost::shared_ptr note); + void add(const boost::shared_ptr note); + void remove(const boost::shared_ptr note); private: - XMLNode &marshal_note(const boost::shared_ptr note); - boost::shared_ptr unmarshal_note(XMLNode *xml_note); + XMLNode &marshal_note(const boost::shared_ptr note); + boost::shared_ptr unmarshal_note(XMLNode *xml_note); boost::shared_ptr _model; const std::string _name; - typedef std::list< boost::shared_ptr > NoteList; + typedef std::list< boost::shared_ptr > NoteList; NoteList _added_notes; NoteList _removed_notes; @@ -146,8 +90,6 @@ public: MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); void apply_command(Command* cmd); - bool edited() const { return _edited; } - void set_edited(bool yn) { _edited = yn; } bool write_to(boost::shared_ptr source); // MidiModel doesn't use the normal AutomationList serialisation code @@ -157,90 +99,11 @@ public: sigc::signal ContentsChanged; - /** Read iterator */ - class const_iterator { - public: - const_iterator(const MidiModel& model, double t); - ~const_iterator(); - - inline bool locked() const { return _locked; } - - const MIDI::Event& operator*() const { return *_event; } - const boost::shared_ptr operator->() const { return _event; } - const boost::shared_ptr get_event_pointer() { return _event; } - - const const_iterator& operator++(); // prefix only - bool operator==(const const_iterator& other) const; - bool operator!=(const const_iterator& other) const { return ! operator==(other); } - - const_iterator& operator=(const const_iterator& other); - - private: - friend class MidiModel; - - const MidiModel* _model; - boost::shared_ptr _event; - - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; - - mutable ActiveNotes _active_notes; - - bool _is_end; - bool _locked; - Notes::const_iterator _note_iter; - std::vector _control_iters; - std::vector::iterator _control_iter; - }; - - const_iterator begin() const { return const_iterator(*this, 0); } - const const_iterator& end() const { return _end_iter; } - const MidiSource* midi_source() const { return _midi_source; } void set_midi_source(MidiSource* source) { _midi_source = source; } - bool control_to_midi_event(boost::shared_ptr& ev, const MidiControlIterator& iter) const; private: friend class DeltaCommand; - void add_note_unlocked(const boost::shared_ptr note); - void remove_note_unlocked(const boost::shared_ptr note); - - friend class const_iterator; - -#ifndef NDEBUG - bool is_sorted() const; -#endif - - void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity); - void append_note_off_unlocked(uint8_t chan, double time, uint8_t note); - void append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte); - void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number); - - mutable Glib::RWLock _lock; - - Notes _notes; - - NoteMode _note_mode; - - typedef std::vector WriteNotes; - WriteNotes _write_notes[16]; - bool _writing; - bool _edited; - - typedef std::vector< boost::shared_ptr > AutomationLists; - AutomationLists _dirty_automations; - - const const_iterator _end_iter; - - mutable nframes_t _next_read; - mutable const_iterator _read_iter; - - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; // We cannot use a boost::shared_ptr here to avoid a retain cycle MidiSource* _midi_source; diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 44f775dc1c..66257372a9 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -80,10 +80,10 @@ class MidiRegion : public Region Controls& controls() { return midi_source()->model()->controls(); } const Controls& controls() const { return midi_source()->model()->controls(); } - boost::shared_ptr control(Parameter id, bool create_if_missing=false) + boost::shared_ptr control(Evoral::Parameter id, bool create_if_missing=false) { return midi_source()->model()->control(id, create_if_missing); } - boost::shared_ptr control(Parameter id) const + boost::shared_ptr control(Evoral::Parameter id) const { return midi_source()->model()->control(id); } int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&); diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index ff0be5c997..db0c3029a8 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace ARDOUR { @@ -243,7 +244,7 @@ MidiRingBufferBase::write(size_t size, const T* src) * * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..] */ -class MidiRingBuffer : public MidiRingBufferBase { +class MidiRingBuffer : public MidiRingBufferBase, public Evoral::EventSink { public: /** @param size Size in bytes. */ @@ -251,8 +252,8 @@ public: : MidiRingBufferBase(size), _channel_mask(0x0000FFFF) {} - size_t write(double time, size_t size, const uint8_t* buf); - bool read(double* time, size_t* size, uint8_t* buf); + size_t write(double time, uint32_t size, const uint8_t* buf); + bool read(double* time, uint32_t* size, uint8_t* buf); bool read_prefix(double* time, size_t* size); bool read_contents(size_t size, uint8_t* buf); @@ -292,7 +293,7 @@ private: inline bool -MidiRingBuffer::read(double* time, size_t* size, uint8_t* buf) +MidiRingBuffer::read(double* time, uint32_t* size, uint8_t* buf) { bool success = MidiRingBufferBase::full_read(sizeof(double), (uint8_t*)time); @@ -333,7 +334,7 @@ MidiRingBuffer::read_contents(size_t size, uint8_t* buf) inline size_t -MidiRingBuffer::write(double time, size_t size, const uint8_t* buf) +MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf) { /*fprintf(stderr, "MRB %p write (t = %f) ", this, time); for (size_t i = 0; i < size; ++i) diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 997f3f9d69..9cb222d207 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -58,7 +58,7 @@ class MidiSource : public Source virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt); - virtual void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev) = 0; + virtual void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) = 0; virtual void mark_for_remove() = 0; virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time); diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 6e4677df22..7eda6f904b 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -75,7 +75,7 @@ public: struct MidiControl : public AutomationControl { MidiControl(MidiTrack* route, boost::shared_ptr al) - : AutomationControl (route->session(), al, al->parameter().to_string()) + : AutomationControl (route->session(), al, al->parameter().symbol()) , _route (route) {} diff --git a/libs/ardour/ardour/note.h b/libs/ardour/ardour/note.h deleted file mode 100644 index 0f649b3370..0000000000 --- a/libs/ardour/ardour/note.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2007 Paul Davis - Author: Dave Robillard - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __ardour_note_h__ -#define __ardour_note_h__ - -#include -#include - -namespace ARDOUR { - - -/** A MIDI Note. - * - * A note is (unfortunately) special and not just another MIDI::Event as it - * has a duration and two separate MIDI events (on and off). - */ -class Note { -public: - Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40); - Note(const Note& copy); - ~Note(); - - const Note& operator=(const Note& copy); - - inline bool operator==(const Note& other) - { return time() == other.time() && - note() == other.note() && - duration() == other.duration() && - velocity() == other.velocity() && - channel() == other.channel(); - } - - inline double time() const { return _on_event.time(); } - inline double end_time() const { return _off_event.time(); } - inline uint8_t note() const { return _on_event.note(); } - inline uint8_t velocity() const { return _on_event.velocity(); } - inline double duration() const { return _off_event.time() - _on_event.time(); } - inline uint8_t channel() const { - assert(_on_event.channel() == _off_event.channel()); - return _on_event.channel(); - } - - inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; } - inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } - inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } - inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; } - inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); } - - inline MIDI::Event& on_event() { return _on_event; } - inline MIDI::Event& off_event() { return _off_event; } - - inline const MIDI::Event& on_event() const { return _on_event; } - inline const MIDI::Event& off_event() const { return _off_event; } - -private: - // Event buffers are self-contained - MIDI::Event _on_event; - MIDI::Event _off_event; -}; - - -} // namespace ARDOUR - -#endif /* __ardour_note_h__ */ diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 2559eed003..1b85495d7a 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -101,14 +101,16 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful float effective_y; float effective_z; - bool _muted; + bool _muted; struct PanControllable : public AutomationControl { PanControllable (Session& s, std::string name, StreamPanner& p, Parameter param) - : AutomationControl (s, boost::shared_ptr(new AutomationList( - param, 0.0, 1.0, 0.5)), name) - , panner (p) { assert(param.type() != NullAutomation); } + : AutomationControl (s, + boost::shared_ptr(new AutomationList(param)), name) + , panner (p) + { assert(param.type() != NullAutomation); } + AutomationList* alist() { return (AutomationList*)_list.get(); } StreamPanner& panner; void set_value (float); diff --git a/libs/ardour/ardour/parameter.h b/libs/ardour/ardour/parameter.h index a5dd9cdca9..dbcccd811f 100644 --- a/libs/ardour/ardour/parameter.h +++ b/libs/ardour/ardour/parameter.h @@ -24,140 +24,86 @@ #include #include #include +#include +#include namespace ARDOUR { - /** ID of an automatable parameter. * * A given automatable object has a number of automatable parameters. This is * the unique ID for those parameters. Anything automatable (AutomationList, - * Curve) must have an ID unique with respect to it's Automatable parent. - * - * A parameter ID has two parts, a type and an int (only used by some types). + * Curve) must have unique Parameter ID with respect to it's Automatable parent. * - * This is a bit more ugly than it could be, due to using the existing/legacy - * ARDOUR::AutomationType: GainAutomation, PanAutomation, SoloAutomation, - * and MuteAutomation use only the type(), but PluginAutomation and - * MidiCCAutomation use the id() as port number and CC number, respectively. + * These are fast to compare, but passing a (const) reference around is + * probably more efficient than copying because the Parameter contains + * metadata not used for comparison. * - * Future types may use a string or URI or whatever, as long as these are - * comparable anything may be added. ints are best as these should be fast to - * copy and compare with one another. + * See evoral/Parameter.hpp for precise definition. */ -class Parameter +class Parameter : public Evoral::Parameter { public: Parameter(AutomationType type = NullAutomation, uint32_t id=0, uint8_t channel=0) - : _type(type), _id(id), _channel(channel) - {} - - Parameter(const std::string& str); - - inline AutomationType type() const { return _type; } - inline uint32_t id() const { return _id; } - inline uint8_t channel() const { return _channel; } - - /** - * Equivalence operator - * It is obvious from the definition that this operator - * is transitive, as required by stict weak ordering - * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html) - */ - inline bool operator==(const Parameter& id) const { - return (_type == id._type && _id == id._id && _channel == id._channel); + : Evoral::Parameter((uint32_t)type, id, channel) + { + init(type); } - /** Strict weak ordering - * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html) - * This is necessary so that std::set works): - * Sort Parameters first according to type then to id and lastly to channel. - * - * Proof: - *
    - *
  1. Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.
  2. - * - *
  3. Antisymmetry: given x != y, f(x, y) implies !f(y, x) because of the same - * property of \c < in each branch and the symmetry of operator==.
  4. - * - *
  5. Transitivity: let f(x, y) and f(y, z) be true. We prove by assuming the contrary, - * that f(x, z) does not hold. - * That would imply exactly one of the following: - *
      - *
    1. x == z which contradicts the assumption f(x, y) and f(y, x) - * because of antisymmetry. - *
    2. - *
    3. f(z, x) is true. That would imply that one of the ivars (we call it i) - * of x is greater than the same ivar in z while all "previous" ivars - * are equal. That would imply that also in y all those "previous" - * ivars are equal and because if x.i > z.i it is impossible - * that there is an y that satisfies x.i < y.i < z.i at the same - * time which contradicts the assumption. - *
    4. - *
    - *
  6. - *
- */ - inline bool operator<(const Parameter& id) const { -#ifndef NDEBUG - if (_type == NullAutomation) - PBD::warning << "Uninitialized Parameter compared." << endmsg; -#endif - if (_type < id._type) { - return true; - } else if (_type == id._type && _id < id._id) { - return true; - } else if (_id == id._id && _channel < id._channel) { - return true; - } - - return false; + Parameter(AutomationType type, double min, double max, double normal) + : Evoral::Parameter((uint32_t)type, 0, 0, min, max, normal) + {} + + Parameter(const Evoral::Parameter& copy) + : Evoral::Parameter(copy) + { + init((AutomationType)_type); } - inline operator bool() const { return (_type != 0); } - - std::string to_string() const; - - /* The below properties are only used for CC right now, but unchanging properties - * of parameters (rather than changing parameters of automation lists themselves) - * should be moved here */ - - inline double min() const { - switch(_type) { + void init(AutomationType type) { + _normal = 0.0f; + switch(type) { + case NullAutomation: + case GainAutomation: + _min = 0.0f; + _max = 2.0f; + _normal = 1.0f; + break; + case PanAutomation: + _min = 0.0f; + _max = 1.0f; + _normal = 0.5f; + case PluginAutomation: + case SoloAutomation: + case MuteAutomation: + case FadeInAutomation: + case FadeOutAutomation: + case EnvelopeAutomation: + _min = 0.0f; + _max = 2.0f; + _normal = 1.0f; case MidiCCAutomation: + Evoral::MIDI::ContinuousController::set_range(*this); break; case MidiPgmChangeAutomation: + Evoral::MIDI::ProgramChange::set_range(*this); break; case MidiPitchBenderAutomation: + Evoral::MIDI::PitchBender::set_range(*this); break; case MidiChannelAftertouchAutomation: - return 0.0; - - default: - return DBL_MIN; + Evoral::MIDI::ChannelAftertouch::set_range(*this); break; } } - inline double max() const { - switch(_type) { - case MidiCCAutomation: - case MidiPgmChangeAutomation: - case MidiChannelAftertouchAutomation: - return 127.0; - case MidiPitchBenderAutomation: - return 16383.0; - - default: - return DBL_MAX; - } - } + Parameter(const std::string& str); + + inline AutomationType type() const { return (AutomationType)_type; } + + std::string symbol() const; inline bool is_integer() const { return (_type >= MidiCCAutomation && _type <= MidiChannelAftertouchAutomation); } -private: - // default copy constructor is ok - AutomationType _type; - uint32_t _id; - uint8_t _channel; + inline operator Parameter() { return (Parameter)*this; } }; diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index c19d113256..8db9fb14fe 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -77,7 +77,7 @@ class PluginInsert : public Processor void set_parameter (Parameter param, float val); float get_parameter (Parameter param); - float default_parameter_value (Parameter param); + float default_parameter_value (Evoral::Parameter param); struct PluginControl : public AutomationControl { diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 88bf1e5d13..3217c8e3e8 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -26,6 +26,8 @@ #include +namespace Evoral { class Event; } + namespace ARDOUR { class MidiRingBuffer; @@ -71,7 +73,7 @@ class SMFSource : public MidiSource { void set_allow_remove_if_empty (bool yn); void mark_for_remove(); - void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev); + void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev); int flush_header (); int flush_footer (); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index cd05e5fc86..532abeb123 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -604,9 +604,9 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ if (!diskstream->record_enabled() && _session.transport_rolling()) { - Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK); + Glib::Mutex::Lock am (_control_lock, Glib::TRY_LOCK); - if (am.locked() && gain_control()->list()->automation_playback()) { + if (am.locked() && gain_control()->automation_playback()) { apply_gain_automation = gain_control()->list()->curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); } } @@ -702,7 +702,7 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes } } - if (gain_control()->list()->automation_state() == Play) { + if (gain_control()->automation_state() == Play) { gain_control()->list()->curve().get_vector (start, start + nframes, gain_automation, nframes); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 271544297d..707f10e91a 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -77,9 +77,9 @@ AudioRegion::init () /* constructor for use by derived types only */ AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name) : Region (s, start, length, name, DataType::AUDIO) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); } @@ -87,9 +87,9 @@ AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string /** Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, nframes_t length) : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0, Region::Flag(Region::DefaultFlags|Region::External)) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -102,9 +102,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n /* Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (src, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -117,9 +117,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n /* Basic AudioRegion constructor (many channels) */ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (srcs, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); listen_to_my_sources (); @@ -128,9 +128,9 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len /** Create a new AudioRegion, that is part of an existing one */ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (other, offset, length, name, layer, flags) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { set > unique_srcs; @@ -180,9 +180,9 @@ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t AudioRegion::AudioRegion (boost::shared_ptr other) : Region (other) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { assert(_type == DataType::AUDIO); _scale_amplitude = other->_scale_amplitude; @@ -196,9 +196,9 @@ AudioRegion::AudioRegion (boost::shared_ptr other) AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& node) : Region (src, node) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { boost::shared_ptr afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -217,9 +217,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) : Region (srcs, node) - , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0)) - , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) - , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) + , _fade_in (new AutomationList(Parameter(FadeInAutomation))) + , _fade_out (new AutomationList(Parameter(FadeOutAutomation))) + , _envelope (new AutomationList(Parameter(EnvelopeAutomation))) { init (); diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index cacebe59a4..349c09e136 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -92,7 +92,7 @@ Automatable::load_automation (const string& path) return 1; } - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); set tosave; _controls.clear (); @@ -108,7 +108,7 @@ Automatable::load_automation (const string& path) in >> value; if (!in) goto bad; /* FIXME: this is legacy and only used for plugin inserts? I think? */ - boost::shared_ptr c = control (Parameter(PluginAutomation, port), true); + boost::shared_ptr c = control (Parameter(PluginAutomation, port), true); c->list()->add (when, value); tosave.insert (Parameter(PluginAutomation, port)); } @@ -122,12 +122,10 @@ Automatable::load_automation (const string& path) } void -Automatable::add_control(boost::shared_ptr ac) +Automatable::add_control(boost::shared_ptr ac) { Parameter param = ac->parameter(); - _controls[param] = ac; - _can_automate_list.insert(param); // Sync everything (derived classes) up to initial values @@ -135,21 +133,9 @@ Automatable::add_control(boost::shared_ptr ac) } void -Automatable::what_has_automation (set& s) const -{ - Glib::Mutex::Lock lm (_automation_lock); - Controls::const_iterator li; - - // FIXME: correct semantics? - for (li = _controls.begin(); li != _controls.end(); ++li) { - s.insert ((*li).first); - } -} - -void -Automatable::what_has_visible_automation (set& s) const +Automatable::what_has_visible_data (set& s) const { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); set::const_iterator li; for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) { @@ -157,42 +143,6 @@ Automatable::what_has_visible_automation (set& s) const } } -/** Returns NULL if we don't have an AutomationList for \a parameter. - */ -boost::shared_ptr -Automatable::control (Parameter parameter, bool create_if_missing) -{ - Controls::iterator i = _controls.find(parameter); - - if (i != _controls.end()) { - return i->second; - - } else if (create_if_missing) { - boost::shared_ptr al (new AutomationList ( - parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter))); - boost::shared_ptr ac(control_factory(al)); - add_control(ac); - return ac; - - } else { - //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; - return boost::shared_ptr(); - } -} - -boost::shared_ptr -Automatable::control (Parameter parameter) const -{ - Controls::const_iterator i = _controls.find(parameter); - - if (i != _controls.end()) { - return i->second; - } else { - //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; - return boost::shared_ptr(); - } -} - string Automatable::describe_parameter (Parameter param) { @@ -213,7 +163,7 @@ Automatable::describe_parameter (Parameter param) } else if (param.type() == MidiChannelAftertouchAutomation) { return string_compose("Aftertouch [%1]", int(param.channel()) + 1); } else { - return param.to_string(); + return param.symbol(); } } @@ -237,37 +187,6 @@ Automatable::mark_automation_visible (Parameter what, bool yn) } } -bool -Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const -{ - Controls::const_iterator li; - - next_event.when = max_frames; - - for (li = _controls.begin(); li != _controls.end(); ++li) { - - AutomationList::const_iterator i; - boost::shared_ptr alist (li->second->list()); - ControlEvent cp (now, 0.0f); - - for (i = lower_bound (alist->const_begin(), alist->const_end(), &cp, AutomationList::time_comparator); - i != alist->const_end() && (*i)->when < end; ++i) { - if ((*i)->when > now) { - break; - } - } - - if (i != alist->const_end() && (*i)->when < end) { - - if ((*i)->when < next_event.when) { - next_event.when = (*i)->when; - } - } - } - - return next_event.when != max_frames; -} - /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner) * had a single automation parameter, with it's type implicit. Derived objects should * pass that type and it will be used for the untyped AutomationList found. @@ -275,7 +194,7 @@ Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_e int Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); /* Don't clear controls, since some may be special derived Controllable classes */ @@ -301,11 +220,11 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) if (!id_prop) { warning << "AutomationList node without automation-id property, " - << "using default: " << legacy_param.to_string() << endmsg; + << "using default: " << legacy_param.symbol() << endmsg; al->set_parameter(legacy_param); } - boost::shared_ptr existing = control(param); + boost::shared_ptr existing = control(param); if (existing) existing->set_list(al); else @@ -324,7 +243,7 @@ Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param) XMLNode& Automatable::get_automation_state () { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); XMLNode* node = new XMLNode (X_("Automation")); if (_controls.empty()) { @@ -332,30 +251,24 @@ Automatable::get_automation_state () } for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) { - node->add_child_nocopy (li->second->list()->get_state ()); + boost::shared_ptr l + = boost::dynamic_pointer_cast(li->second->list()); + node->add_child_nocopy (l->get_state ()); } return *node; } -void -Automatable::clear_automation () -{ - Glib::Mutex::Lock lm (_automation_lock); - - for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) - li->second->list()->clear(); -} - void Automatable::set_parameter_automation_state (Parameter param, AutoState s) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control (param, true); + boost::shared_ptr c = control (param, true); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - if (s != c->list()->automation_state()) { - c->list()->set_automation_state (s); + if (s != l->automation_state()) { + l->set_automation_state (s); _session.set_dirty (); } } @@ -366,15 +279,16 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock) AutoState result = Off; if (lock) - _automation_lock.lock(); + _control_lock.lock(); - boost::shared_ptr c = control(param); + boost::shared_ptr c = control(param); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); if (c) - result = c->list()->automation_state(); + result = l->automation_state(); if (lock) - _automation_lock.unlock(); + _control_lock.unlock(); return result; } @@ -382,12 +296,13 @@ Automatable::get_parameter_automation_state (Parameter param, bool lock) void Automatable::set_parameter_automation_style (Parameter param, AutoStyle s) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control(param, true); + boost::shared_ptr c = control(param, true); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - if (s != c->list()->automation_style()) { - c->list()->set_automation_style (s); + if (s != l->automation_style()) { + l->set_automation_style (s); _session.set_dirty (); } } @@ -395,12 +310,13 @@ Automatable::set_parameter_automation_style (Parameter param, AutoStyle s) AutoStyle Automatable::get_parameter_automation_style (Parameter param) { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (_control_lock); - boost::shared_ptr c = control(param); + boost::shared_ptr c = control(param); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); if (c) { - return c->list()->automation_style(); + return l->automation_style(); } else { return Absolute; // whatever } @@ -409,20 +325,22 @@ Automatable::get_parameter_automation_style (Parameter param) void Automatable::protect_automation () { - set automated_params; + typedef set ParameterSet; + ParameterSet automated_params; - what_has_automation (automated_params); + what_has_data (automated_params); - for (set::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { + for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { - boost::shared_ptr c = control(*i); + boost::shared_ptr c = control(*i); + boost::shared_ptr l = boost::dynamic_pointer_cast(c->list()); - switch (c->list()->automation_state()) { + switch (l->automation_state()) { case Write: - c->list()->set_automation_state (Off); + l->set_automation_state (Off); break; case Touch: - c->list()->set_automation_state (Play); + l->set_automation_state (Play); break; default: break; @@ -436,8 +354,10 @@ Automatable::automation_snapshot (nframes_t now, bool force) if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) { - if (i->second->list()->automation_write()) { - i->second->list()->rt_add (now, i->second->user_value()); + boost::shared_ptr c + = boost::dynamic_pointer_cast(i->second); + if (c->automation_write()) { + c->list()->rt_add (now, i->second->user_value()); } } @@ -450,29 +370,34 @@ Automatable::transport_stopped (nframes_t now) { for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) { - boost::shared_ptr c = li->second; + boost::shared_ptr c + = boost::dynamic_pointer_cast(li->second); + boost::shared_ptr l + = boost::dynamic_pointer_cast(c->list()); c->list()->reposition_for_rt_add (now); - if (c->list()->automation_state() != Off) { + if (c->automation_state() != Off) { c->set_value(c->list()->eval(now)); } } } -/* FIXME: this probably doesn't belong here */ -boost::shared_ptr -Automatable::control_factory(boost::shared_ptr list) +boost::shared_ptr +Automatable::control_factory(boost::shared_ptr list) const { - if ( - list->parameter().type() == MidiCCAutomation || - list->parameter().type() == MidiPgmChangeAutomation || - list->parameter().type() == MidiChannelAftertouchAutomation - ) { - // FIXME: this will die horribly if this is not a MidiTrack - return boost::shared_ptr(new MidiTrack::MidiControl((MidiTrack*)this, list)); + boost::shared_ptr l = boost::dynamic_pointer_cast(list); + assert(l); + if (l->parameter().type() >= MidiCCAutomation + && l->parameter().type() <= MidiChannelAftertouchAutomation) { + return boost::shared_ptr(new MidiTrack::MidiControl((MidiTrack*)this, l)); } else { - return boost::shared_ptr(new AutomationControl(_session, list)); + return boost::shared_ptr(new AutomationControl(_session, l)); } } +boost::shared_ptr +Automatable::control_list_factory(const Evoral::Parameter& param) const +{ + return boost::shared_ptr(new AutomationList(param)); +} diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index 4885a6fed9..a16306838a 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -30,10 +30,9 @@ using namespace PBD; AutomationControl::AutomationControl(Session& session, boost::shared_ptr list, string name) - : Controllable((name == "unnamed controllable") ? list->parameter().to_string() : name) + : Controllable((name == "unnamed controllable") ? list->parameter().symbol() : name) + , Evoral::Control(list) , _session(session) - , _list(list) - , _user_value(list->default_value()) { } @@ -43,49 +42,27 @@ AutomationControl::AutomationControl(Session& session, boost::shared_ptrautomation_playback()) - return _list->eval(_session.transport_frame()); - else - return _user_value; + bool from_list = ((AutomationList*)_list.get())->automation_playback(); + return Control::get_value(from_list, _session.transport_frame()); } void AutomationControl::set_value(float value) { - _user_value = value; + bool to_list = _session.transport_stopped() + && ((AutomationList*)_list.get())->automation_playback(); - if (_session.transport_stopped() && _list->automation_write()) - _list->add(_session.transport_frame(), value); + Control::set_value(value, to_list, _session.transport_frame()); Changed(); /* EMIT SIGNAL */ } -/** Get the latest user-set value, which may not equal get_value() when automation - * is playing back, etc. - * - * Automation write/touch works by periodically sampling this value and adding it - * to the AutomationList. - */ -float -AutomationControl::user_value() const -{ - return _user_value; -} - - void -AutomationControl::set_list(boost::shared_ptr list) +AutomationControl::set_list(boost::shared_ptr list) { - _list = list; - _user_value = list->default_value(); + Control::set_list(list); Changed(); /* EMIT SIGNAL */ } - -Parameter -AutomationControl::parameter() const -{ - return _list->parameter(); -} diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index af390953f4..2d98f1c27c 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -39,11 +39,6 @@ using namespace PBD; sigc::signal AutomationList::AutomationListCreated; -static bool sort_events_by_time (ControlEvent* a, ControlEvent* b) -{ - return a->when < b->when; -} - #if 0 static void dumpit (const AutomationList& al, string prefix = "") { @@ -56,92 +51,33 @@ static void dumpit (const AutomationList& al, string prefix = "") #endif /* XXX: min_val max_val redundant? (param.min() param.max()) */ -AutomationList::AutomationList (Parameter id, double min_val, double max_val, double default_val) - : _parameter(id) - , _interpolation(Linear) - , _curve(new Curve(*this)) -{ - _parameter = id; - _frozen = 0; - _changed_when_thawed = false; +AutomationList::AutomationList (Parameter id) + : ControlList(id) +{ _state = Off; _style = Absolute; - _min_yval = min_val; - _max_yval = max_val; _touching = false; - _max_xval = 0; // means "no limit" - _default_value = default_val; - _rt_insertion_point = _events.end(); - _lookup_cache.left = -1; - _lookup_cache.range.first = _events.end(); - _search_cache.left = -1; - _search_cache.range.first = _events.end(); - _sort_pending = false; assert(_parameter.type() != NullAutomation); AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) - : _parameter(other._parameter) - , _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(other) { - _frozen = 0; - _changed_when_thawed = false; - _style = other._style; - _min_yval = other._min_yval; - _max_yval = other._max_yval; - _max_xval = other._max_xval; - _default_value = other._default_value; _state = other._state; _touching = other._touching; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; - - for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { - _events.push_back (new ControlEvent (**i)); - } - - mark_dirty (); + assert(_parameter.type() != NullAutomation); AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other, double start, double end) - : _parameter(other._parameter) - , _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(other) { - _frozen = 0; - _changed_when_thawed = false; _style = other._style; - _min_yval = other._min_yval; - _max_yval = other._max_yval; - _max_xval = other._max_xval; - _default_value = other._default_value; _state = other._state; _touching = other._touching; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; - - /* now grab the relevant points, and shift them back if necessary */ - - AutomationList* section = const_cast(&other)->copy (start, end); - - if (!section->empty()) { - for (iterator i = section->begin(); i != section->end(); ++i) { - _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); - } - } - - delete section; - - mark_dirty (); assert(_parameter.type() != NullAutomation); AutomationListCreated(this); @@ -151,21 +87,11 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl * in or below the node. It is used if \a id is non-null. */ AutomationList::AutomationList (const XMLNode& node, Parameter id) - : _interpolation(Linear) - , _curve(new Curve(*this)) + : ControlList(id) { - _frozen = 0; - _changed_when_thawed = false; _touching = false; - _min_yval = FLT_MIN; - _max_yval = FLT_MAX; - _max_xval = 0; // means "no limit" _state = Off; _style = Absolute; - _rt_insertion_point = _events.end(); - _lookup_cache.range.first = _events.end(); - _search_cache.range.first = _events.end(); - _sort_pending = false; set_state (node); @@ -179,10 +105,12 @@ AutomationList::AutomationList (const XMLNode& node, Parameter id) AutomationList::~AutomationList() { GoingAway (); - - for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) { - delete (*x); - } +} + +boost::shared_ptr +AutomationList::create(Evoral::Parameter id) +{ + return boost::shared_ptr(new AutomationList(id)); } bool @@ -217,12 +145,10 @@ AutomationList::operator= (const AutomationList& other) void AutomationList::maybe_signal_changed () { - mark_dirty (); + ControlList::maybe_signal_changed (); - if (_frozen) { - _changed_when_thawed = true; - } else { - StateChanged (); + if (!_frozen) { + StateChanged (); /* EMIT SIGNAL */ } } @@ -248,390 +174,14 @@ void AutomationList::start_touch () { _touching = true; - _new_touch = true; + _new_value = true; } void AutomationList::stop_touch () { _touching = false; - _new_touch = false; -} - -void -AutomationList::clear () -{ - { - Glib::Mutex::Lock lm (_lock); - _events.clear (); - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::x_scale (double factor) -{ - Glib::Mutex::Lock lm (_lock); - _x_scale (factor); -} - -bool -AutomationList::extend_to (double when) -{ - Glib::Mutex::Lock lm (_lock); - if (_events.empty() || _events.back()->when == when) { - return false; - } - double factor = when / _events.back()->when; - _x_scale (factor); - return true; -} - -void AutomationList::_x_scale (double factor) -{ - for (iterator i = _events.begin(); i != _events.end(); ++i) { - (*i)->when = floor ((*i)->when * factor); - } - - mark_dirty (); -} - -void -AutomationList::reposition_for_rt_add (double when) -{ - _rt_insertion_point = _events.end(); -} - -void -AutomationList::rt_add (double when, double value) -{ - /* this is for automation recording */ - - if ((_state & Touch) && !_touching) { - return; - } - - // 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_touch) { - 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 { - - where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); - - if (where != _events.end()) { - if ((*where)->when == when) { - (*where)->value = value; - done = true; - } - } - } - - if (!done) { - _rt_insertion_point = _events.insert (where, new ControlEvent (when, value)); - } - - _new_touch = false; - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::fast_simple_add (double when, double value) -{ - /* to be used only for loading pre-sorted data from saved state */ - _events.insert (_events.end(), new ControlEvent (when, value)); - assert(_events.back()); -} - -void -AutomationList::add (double when, double value) -{ - /* this is for graphical editing */ - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (when, 0.0f); - bool insert = true; - iterator insertion_point; - - for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) { - - /* only one point allowed per time point */ - - if ((*insertion_point)->when == when) { - (*insertion_point)->value = value; - insert = false; - break; - } - - if ((*insertion_point)->when >= when) { - break; - } - } - - if (insert) { - - _events.insert (insertion_point, new ControlEvent (when, value)); - reposition_for_rt_add (0); - - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::erase (iterator i) -{ - { - Glib::Mutex::Lock lm (_lock); - _events.erase (i); - reposition_for_rt_add (0); - mark_dirty (); - } - maybe_signal_changed (); -} - -void -AutomationList::erase (iterator start, iterator end) -{ - { - Glib::Mutex::Lock lm (_lock); - _events.erase (start, end); - reposition_for_rt_add (0); - mark_dirty (); - } - maybe_signal_changed (); -} - -void -AutomationList::reset_range (double start, double endt) -{ - bool reset = false; - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - for (iterator i = s; i != e; ++i) { - (*i)->value = _default_value; - } - - reset = true; - - mark_dirty (); - } - } - - if (reset) { - maybe_signal_changed (); - } -} - -void -AutomationList::erase_range (double start, double endt) -{ - bool erased = false; - - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (start, 0.0f); - iterator s; - iterator e; - - if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) { - cp.when = endt; - e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - _events.erase (s, e); - reposition_for_rt_add (0); - erased = true; - mark_dirty (); - } - - } - - if (erased) { - maybe_signal_changed (); - } -} - -void -AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta) -{ - /* note: we assume higher level logic is in place to avoid this - reordering the time-order of control events in the list. ie. all - points after end are later than (end)->when. - */ - - { - Glib::Mutex::Lock lm (_lock); - - while (start != end) { - (*start)->when += xdelta; - (*start)->value += ydelta; - if (isnan ((*start)->value)) { - abort (); - } - ++start; - } - - if (!_frozen) { - _events.sort (sort_events_by_time); - } else { - _sort_pending = true; - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -void -AutomationList::slide (iterator before, double distance) -{ - { - Glib::Mutex::Lock lm (_lock); - - if (before == _events.end()) { - return; - } - - while (before != _events.end()) { - (*before)->when += distance; - ++before; - } - } - - maybe_signal_changed (); -} - -void -AutomationList::modify (iterator iter, double when, double val) -{ - /* note: we assume higher level logic is in place to avoid this - reordering the time-order of control events in the list. ie. all - points after *iter are later than when. - */ - - { - Glib::Mutex::Lock lm (_lock); - - (*iter)->when = when; - (*iter)->value = val; - - if (isnan (val)) { - abort (); - } - - if (!_frozen) { - _events.sort (sort_events_by_time); - } else { - _sort_pending = true; - } - - mark_dirty (); - } - - maybe_signal_changed (); -} - -std::pair -AutomationList::control_points_adjacent (double xval) -{ - Glib::Mutex::Lock lm (_lock); - iterator i; - ControlEvent cp (xval, 0.0f); - std::pair ret; - - ret.first = _events.end(); - ret.second = _events.end(); - - for (i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); i != _events.end(); ++i) { - - if (ret.first == _events.end()) { - if ((*i)->when >= xval) { - if (i != _events.begin()) { - ret.first = i; - --ret.first; - } else { - return ret; - } - } - } - - if ((*i)->when > xval) { - ret.second = i; - break; - } - } - - return ret; + _new_value = false; } void @@ -643,790 +193,20 @@ AutomationList::freeze () void AutomationList::thaw () { - if (_frozen == 0) { - PBD::stacktrace (cerr); - fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg; - /*NOTREACHED*/ - } - - if (--_frozen > 0) { - return; - } - - { - Glib::Mutex::Lock lm (_lock); - - if (_sort_pending) { - _events.sort (sort_events_by_time); - _sort_pending = false; - } - } + ControlList::thaw(); if (_changed_when_thawed) { StateChanged(); /* EMIT SIGNAL */ } } -void -AutomationList::set_max_xval (double x) -{ - _max_xval = x; -} - void -AutomationList::mark_dirty () +AutomationList::mark_dirty () const { - _lookup_cache.left = -1; - _search_cache.left = -1; + ControlList::mark_dirty (); Dirty (); /* EMIT SIGNAL */ } -void -AutomationList::truncate_end (double last_coordinate) -{ - { - Glib::Mutex::Lock lm (_lock); - ControlEvent cp (last_coordinate, 0); - AutomationList::reverse_iterator i; - double last_val; - - if (_events.empty()) { - return; - } - - if (last_coordinate == _events.back()->when) { - return; - } - - if (last_coordinate > _events.back()->when) { - - /* extending end: - */ - - iterator foo = _events.begin(); - bool lessthantwo; - - if (foo == _events.end()) { - lessthantwo = true; - } else if (++foo == _events.end()) { - lessthantwo = true; - } else { - lessthantwo = false; - } - - if (lessthantwo) { - /* less than 2 points: add a new point */ - _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); - } else { - - /* more than 2 points: check to see if the last 2 values - are equal. if so, just move the position of the - last point. otherwise, add a new point. - */ - - iterator penultimate = _events.end(); - --penultimate; /* points at last point */ - --penultimate; /* points at the penultimate point */ - - if (_events.back()->value == (*penultimate)->value) { - _events.back()->when = last_coordinate; - } else { - _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); - } - } - - } else { - - /* shortening end */ - - last_val = unlocked_eval (last_coordinate); - last_val = max ((double) _min_yval, last_val); - last_val = min ((double) _max_yval, last_val); - - i = _events.rbegin(); - - /* make i point to the last control point */ - - ++i; - - /* now go backwards, removing control points that are - beyond the new last coordinate. - */ - - uint32_t sz = _events.size(); - - while (i != _events.rend() && sz > 2) { - AutomationList::reverse_iterator tmp; - - tmp = i; - ++tmp; - - if ((*i)->when < last_coordinate) { - break; - } - - _events.erase (i.base()); - --sz; - - i = tmp; - } - - _events.back()->when = last_coordinate; - _events.back()->value = last_val; - } - - reposition_for_rt_add (0); - mark_dirty(); - } - - maybe_signal_changed (); -} - -void -AutomationList::truncate_start (double overall_length) -{ - { - Glib::Mutex::Lock lm (_lock); - iterator i; - double first_legal_value; - double first_legal_coordinate; - - if (_events.empty()) { - fatal << _("programming error:") - << "AutomationList::truncate_start() called on an empty list" - << endmsg; - /*NOTREACHED*/ - return; - } - - if (overall_length == _events.back()->when) { - /* no change in overall length */ - return; - } - - if (overall_length > _events.back()->when) { - - /* growing at front: duplicate first point. shift all others */ - - double shift = overall_length - _events.back()->when; - uint32_t np; - - for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) { - (*i)->when += shift; - } - - if (np < 2) { - - /* less than 2 points: add a new point */ - _events.push_front (new ControlEvent (0, _events.front()->value)); - - } else { - - /* more than 2 points: check to see if the first 2 values - are equal. if so, just move the position of the - first point. otherwise, add a new point. - */ - - iterator second = _events.begin(); - ++second; /* points at the second point */ - - if (_events.front()->value == (*second)->value) { - /* first segment is flat, just move start point back to zero */ - _events.front()->when = 0; - } else { - /* leave non-flat segment in place, add a new leading point. */ - _events.push_front (new ControlEvent (0, _events.front()->value)); - } - } - - } else { - - /* shrinking at front */ - - first_legal_coordinate = _events.back()->when - overall_length; - first_legal_value = unlocked_eval (first_legal_coordinate); - first_legal_value = max (_min_yval, first_legal_value); - first_legal_value = min (_max_yval, first_legal_value); - - /* remove all events earlier than the new "front" */ - - i = _events.begin(); - - while (i != _events.end() && !_events.empty()) { - AutomationList::iterator tmp; - - tmp = i; - ++tmp; - - if ((*i)->when > first_legal_coordinate) { - break; - } - - _events.erase (i); - - i = tmp; - } - - - /* shift all remaining points left to keep their same - relative position - */ - - for (i = _events.begin(); i != _events.end(); ++i) { - (*i)->when -= first_legal_coordinate; - } - - /* add a new point for the interpolated new value */ - - _events.push_front (new ControlEvent (0, first_legal_value)); - } - - reposition_for_rt_add (0); - - mark_dirty(); - } - - maybe_signal_changed (); -} - -double -AutomationList::unlocked_eval (double x) const -{ - pair range; - int32_t npoints; - double lpos, upos; - double lval, uval; - double fraction; - - npoints = _events.size(); - - switch (npoints) { - case 0: - return _default_value; - - case 1: - if (x >= _events.front()->when) { - return _events.front()->value; - } else { - // return _default_value; - return _events.front()->value; - } - - case 2: - if (x >= _events.back()->when) { - return _events.back()->value; - } else if (x == _events.front()->when) { - return _events.front()->value; - } else if (x < _events.front()->when) { - // return _default_value; - return _events.front()->value; - } - - lpos = _events.front()->when; - lval = _events.front()->value; - upos = _events.back()->when; - uval = _events.back()->value; - - if (_interpolation == Discrete) - return lval; - - /* linear interpolation betweeen the two points - */ - - fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); - - default: - - if (x >= _events.back()->when) { - return _events.back()->value; - } else if (x == _events.front()->when) { - return _events.front()->value; - } else if (x < _events.front()->when) { - // return _default_value; - return _events.front()->value; - } - - return multipoint_eval (x); - break; - } - - /*NOTREACHED*/ /* stupid gcc */ - return 0.0; -} - -double -AutomationList::multipoint_eval (double x) const -{ - double upos, lpos; - double uval, lval; - double fraction; - - /* "Stepped" lookup (no interpolation) */ - /* FIXME: no cache. significant? */ - if (_interpolation == Discrete) { - const ControlEvent cp (x, 0); - EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); - - // shouldn't have made it to multipoint_eval - assert(i != _events.end()); - - if (i == _events.begin() || (*i)->when == x) - return (*i)->value; - else - return (*(--i))->value; - } - - /* Only do the range lookup if x is in a different range than last time - * this was called (or if the lookup cache has been marked "dirty" (left<0) */ - if ((_lookup_cache.left < 0) || - ((_lookup_cache.left > x) || - (_lookup_cache.range.first == _events.end()) || - ((*_lookup_cache.range.second)->when < x))) { - - const ControlEvent cp (x, 0); - - _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator); - } - - pair range = _lookup_cache.range; - - if (range.first == range.second) { - - /* x does not exist within the list as a control point */ - - _lookup_cache.left = x; - - if (range.first != _events.begin()) { - --range.first; - lpos = (*range.first)->when; - lval = (*range.first)->value; - } else { - /* we're before the first point */ - // return _default_value; - return _events.front()->value; - } - - if (range.second == _events.end()) { - /* we're after the last point */ - return _events.back()->value; - } - - upos = (*range.second)->when; - uval = (*range.second)->value; - - /* linear interpolation betweeen the two points - on either side of x - */ - - fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); - - } - - /* x is a control point in the data */ - _lookup_cache.left = -1; - return (*range.first)->value; -} - -void -AutomationList::build_search_cache_if_necessary(double start, double end) const -{ - /* Only do the range lookup if x is in a different range than last time - * this was called (or if the search cache has been marked "dirty" (left<0) */ - if (!_events.empty() && ((_search_cache.left < 0) || - ((_search_cache.left > start) || - (_search_cache.right < end)))) { - - const ControlEvent start_point (start, 0); - const ControlEvent end_point (end, 0); - - //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := (" - // << start << ".." << end << ")" << endl; - - _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator); - _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, time_comparator); - - _search_cache.left = start; - _search_cache.right = end; - } -} - -/** Get the earliest event between \a start and \a end, using the current interpolation style. - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const -{ - // FIXME: It would be nice if this was unnecessary.. - Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK); - if (!lm.locked()) { - return false; - } - - return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive); -} - - -/** Get the earliest event between \a start and \a end, using the current interpolation style. - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const -{ - if (_interpolation == Discrete) - return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive); - else - return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive); -} - - -/** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation) - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const -{ - build_search_cache_if_necessary(start, end); - - const pair& range = _search_cache.range; - - if (range.first != _events.end()) { - const ControlEvent* const first = *range.first; - - const bool past_start = (inclusive ? first->when >= start : first->when > start); - - /* Earliest points is in range, return it */ - if (past_start >= start && first->when < end) { - - x = first->when; - y = first->value; - - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - ++_search_cache.range.first; - - assert(x >= start); - assert(x < end); - return true; - - } else { - return false; - } - - /* No points in range */ - } else { - return false; - } -} - -/** Get the earliest time the line crosses an integer (Linear interpolation). - * - * If an event is found, \a x and \a y are set to its coordinates. - * - * \param inclusive Include events with timestamp exactly equal to \a start - * \return true if event is found (and \a x and \a y are valid). - */ -bool -AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const -{ - //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl; - - if (_events.size() == 0) - return false; - else if (_events.size() == 1) - return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive); - - // Hack to avoid infinitely repeating the same event - build_search_cache_if_necessary(start, end); - - pair range = _search_cache.range; - - if (range.first != _events.end()) { - - const ControlEvent* first = NULL; - const ControlEvent* next = NULL; - - /* Step is after first */ - if (range.first == _events.begin() || (*range.first)->when == start) { - first = *range.first; - next = *(++range.first); - ++_search_cache.range.first; - - /* Step is before first */ - } else { - const_iterator prev = range.first; - --prev; - first = *prev; - next = *range.first; - } - - if (inclusive && first->when == start) { - x = first->when; - y = first->value; - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - //++_search_cache.range.first; - return true; - } - - if (abs(first->value - next->value) <= 1) { - if (next->when <= end && (!inclusive || next->when > start)) { - x = next->when; - y = next->value; - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - //++_search_cache.range.first; - return true; - } else { - return false; - } - } - - const double slope = (next->value - first->value) / (double)(next->when - first->when); - //cerr << "start y: " << start_y << endl; - - //y = first->value + (slope * fabs(start - first->when)); - y = first->value; - - if (first->value < next->value) // ramping up - y = ceil(y); - else // ramping down - y = floor(y); - - x = first->when + (y - first->value) / (double)slope; - - while ((inclusive && x < start) || (x <= start && y != next->value)) { - - if (first->value < next->value) // ramping up - y += 1.0; - else // ramping down - y -= 1.0; - - x = first->when + (y - first->value) / (double)slope; - } - - /*cerr << first->value << " @ " << first->when << " ... " - << next->value << " @ " << next->when - << " = " << y << " @ " << x << endl;*/ - - assert( (y >= first->value && y <= next->value) - || (y <= first->value && y >= next->value) ); - - - const bool past_start = (inclusive ? x >= start : x > start); - if (past_start && x < end) { - /* Move left of cache to this point - * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; - - return true; - - } else { - return false; - } - - /* No points in the future, so no steps (towards them) in the future */ - } else { - return false; - } -} - -AutomationList* -AutomationList::cut (iterator start, iterator end) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - - { - 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; -} - -AutomationList* -AutomationList::cut_copy_clear (double start, double end, int op) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - iterator s, e; - ControlEvent cp (start, 0.0); - bool changed = false; - - { - Glib::Mutex::Lock lm (_lock); - - 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; - - tmp = x; - ++tmp; - - changed = true; - - /* 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; - } - - if (op != 2 && nal->_events.back()->when != end - start) { - nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end))); - } - - if (changed) { - reposition_for_rt_add (0); - } - - mark_dirty (); - } - - maybe_signal_changed (); - - return nal; - -} - -AutomationList* -AutomationList::copy (iterator start, iterator end) -{ - AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value); - - { - 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; -} - -AutomationList* -AutomationList::cut (double start, double end) -{ - return cut_copy_clear (start, end, 0); -} - -AutomationList* -AutomationList::copy (double start, double end) -{ - return cut_copy_clear (start, end, 1); -} - -void -AutomationList::clear (double start, double end) -{ - (void) cut_copy_clear (start, end, 2); -} - -bool -AutomationList::paste (AutomationList& alist, double pos, float times) -{ - if (alist._events.empty()) { - return false; - } - - { - Glib::Mutex::Lock lm (_lock); - iterator where; - iterator prev; - double end = 0; - ControlEvent cp (pos, 0.0); - - where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); - - for (iterator i = alist.begin();i != alist.end(); ++i) { - _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value)); - end = (*i)->when + pos; - } - - - /* move all points after the insertion along the timeline by - the correct amount. - */ - - while (where != _events.end()) { - iterator tmp; - if ((*where)->when <= end) { - tmp = where; - ++tmp; - _events.erase(where); - where = tmp; - - } else { - break; - } - } - - reposition_for_rt_add (0); - mark_dirty (); - } - - maybe_signal_changed (); - return true; -} - XMLNode& AutomationList::get_state () { @@ -1440,7 +220,7 @@ AutomationList::state (bool full) char buf[64]; LocaleGuard lg (X_("POSIX")); - root->add_property ("automation-id", _parameter.to_string()); + root->add_property ("automation-id", _parameter.symbol()); root->add_property ("id", _id.to_s()); diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 213e7504b0..b6ca322c73 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr in, boost::shared_ptr