diff options
author | Carl Hetherington <carl@carlh.net> | 2012-06-16 17:20:10 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2012-06-16 17:20:10 +0000 |
commit | 5ac22e9095d5f851b5238029e3f813bc86fe2280 (patch) | |
tree | 51460c63aa9ace692dedceb01494f2a92d4bc364 /libs | |
parent | 9429401f11ffa0ee1729dbdc5e14e87cf06e2dc1 (diff) |
Add new SharedStatefulProperty which manages a shared_ptr to
some Stateful object, and a subclass to use this for
AutomationList. SharedStatefulProperty will manage undo / redo
using full copies of the XML state, like MementoCommand,
but does it within the Property undo system.
git-svn-id: svn://localhost/ardour2/branches/3.0@12740 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/audioregion.h | 41 | ||||
-rw-r--r-- | libs/ardour/ardour/automation_list.h | 25 | ||||
-rw-r--r-- | libs/ardour/audioregion.cc | 110 | ||||
-rw-r--r-- | libs/ardour/automation_list.cc | 21 | ||||
-rw-r--r-- | libs/ardour/globals.cc | 29 | ||||
-rw-r--r-- | libs/ardour/test/automation_list_property_test.cc | 123 | ||||
-rw-r--r-- | libs/ardour/test/automation_list_property_test.h | 32 | ||||
-rw-r--r-- | libs/ardour/test/data/automation_list_property_test1.ref | 17 | ||||
-rw-r--r-- | libs/ardour/test/data/automation_list_property_test2.ref | 24 | ||||
-rw-r--r-- | libs/ardour/test/data/automation_list_property_test3.ref | 11 | ||||
-rw-r--r-- | libs/ardour/test/data/automation_list_property_test4.ref | 13 | ||||
-rw-r--r-- | libs/ardour/test/test_util.cc | 20 | ||||
-rw-r--r-- | libs/ardour/test/test_util.h | 2 | ||||
-rw-r--r-- | libs/ardour/wscript | 2 | ||||
-rw-r--r-- | libs/evoral/evoral/ControlList.hpp | 2 | ||||
-rw-r--r-- | libs/evoral/src/ControlList.cpp | 28 | ||||
-rw-r--r-- | libs/pbd/pbd/properties.h | 115 | ||||
-rw-r--r-- | libs/pbd/xml++.cc | 22 |
18 files changed, 524 insertions, 113 deletions
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 5fdc0ac041..e38c98ea05 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -46,16 +46,11 @@ namespace Properties { extern PBD::PropertyDescriptor<bool> fade_in_active; extern PBD::PropertyDescriptor<bool> fade_out_active; extern PBD::PropertyDescriptor<float> scale_amplitude; - - /* the envelope and fades are not scalar items and so - currently (2010/02) are not stored using Property. - However, these descriptors enable us to notify - about changes to them via PropertyChange. - */ - - extern PBD::PropertyDescriptor<bool> envelope; - extern PBD::PropertyDescriptor<bool> fade_in; - extern PBD::PropertyDescriptor<bool> fade_out; + extern PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_in; + extern PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_in; + extern PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_out; + extern PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_out; + extern PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > envelope; } class Playlist; @@ -99,11 +94,11 @@ class AudioRegion : public Region bool fade_out_is_short() const { return _fade_out_is_short; } void set_fade_out_is_short (bool yn); - boost::shared_ptr<AutomationList> fade_in() { return _fade_in; } - boost::shared_ptr<AutomationList> inverse_fade_in() { return _inverse_fade_in; } - boost::shared_ptr<AutomationList> fade_out() { return _fade_out; } - boost::shared_ptr<AutomationList> inverse_fade_out() { return _inverse_fade_out; } - boost::shared_ptr<AutomationList> envelope() { return _envelope; } + boost::shared_ptr<AutomationList> fade_in() { return _fade_in.val (); } + boost::shared_ptr<AutomationList> inverse_fade_in() { return _inverse_fade_in.val (); } + boost::shared_ptr<AutomationList> fade_out() { return _fade_out.val (); } + boost::shared_ptr<AutomationList> inverse_fade_out() { return _inverse_fade_out.val (); } + boost::shared_ptr<AutomationList> envelope() { return _envelope.val (); } Evoral::Range<framepos_t> body_range () const; @@ -231,15 +226,15 @@ class AudioRegion : public Region void connect_to_analysis_changed (); void connect_to_header_position_offset_changed (); - Automatable _automatable; - boost::shared_ptr<AutomationList> _fade_in; - boost::shared_ptr<AutomationList> _inverse_fade_in; - boost::shared_ptr<AutomationList> _fade_out; - boost::shared_ptr<AutomationList> _inverse_fade_out; - boost::shared_ptr<AutomationList> _envelope; - uint32_t _fade_in_suspended; - uint32_t _fade_out_suspended; + AutomationListProperty _fade_in; + AutomationListProperty _inverse_fade_in; + AutomationListProperty _fade_out; + AutomationListProperty _inverse_fade_out; + AutomationListProperty _envelope; + Automatable _automatable; + uint32_t _fade_in_suspended; + uint32_t _fade_out_suspended; boost::shared_ptr<ARDOUR::Region> get_single_other_xfade_region (bool start) const; diff --git a/libs/ardour/ardour/automation_list.h b/libs/ardour/ardour/automation_list.h index 58d1fe4acf..01c9d0641a 100644 --- a/libs/ardour/ardour/automation_list.h +++ b/libs/ardour/ardour/automation_list.h @@ -29,6 +29,7 @@ #include "pbd/undo.h" #include "pbd/xml++.h" #include "pbd/statefuldestructible.h" +#include "pbd/properties.h" #include "ardour/ardour.h" @@ -36,6 +37,28 @@ namespace ARDOUR { +class AutomationList; + +/** A SharedStatefulProperty for AutomationLists */ +class AutomationListProperty : public PBD::SharedStatefulProperty<AutomationList> +{ +public: + AutomationListProperty (PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > d, Ptr p) + : PBD::SharedStatefulProperty<AutomationList> (d.property_id, p) + {} + + AutomationListProperty (PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > d, Ptr o, Ptr c) + : PBD::SharedStatefulProperty<AutomationList> (d.property_id, o, c) + {} + + PBD::PropertyBase* clone () const; + +private: + /* No copy-construction nor assignment */ + AutomationListProperty (AutomationListProperty const &); + AutomationListProperty& operator= (AutomationListProperty const &); +}; + class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlList { public: @@ -82,6 +105,8 @@ class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlL XMLNode& state (bool full); XMLNode& serialize_events (); + bool operator!= (const AutomationList &) const; + private: void create_curve_if_necessary (); int deserialize_events (const XMLNode&); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 50a38b7bad..4b85819d6e 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -65,6 +65,11 @@ namespace ARDOUR { PBD::PropertyDescriptor<bool> fade_out_is_short; PBD::PropertyDescriptor<bool> fade_in_is_xfade; PBD::PropertyDescriptor<bool> fade_in_is_short; + PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_in; + PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_in; + PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_out; + PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_out; + PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > envelope; } } @@ -164,6 +169,16 @@ AudioRegion::make_property_quarks () DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-is-xfade = %1\n", Properties::fade_in_is_xfade.property_id)); Properties::fade_in_is_short.property_id = g_quark_from_static_string (X_("fade-in-is-short")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-is-short = %1\n", Properties::fade_in_is_short.property_id)); + Properties::fade_in.property_id = g_quark_from_static_string (X_("FadeIn")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for FadeIn = %1\n", Properties::fade_in.property_id)); + Properties::inverse_fade_in.property_id = g_quark_from_static_string (X_("InverseFadeIn")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for InverseFadeIn = %1\n", Properties::inverse_fade_in.property_id)); + Properties::fade_out.property_id = g_quark_from_static_string (X_("FadeOut")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for FadeOut = %1\n", Properties::fade_out.property_id)); + Properties::inverse_fade_out.property_id = g_quark_from_static_string (X_("InverseFadeOut")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for InverseFadeOut = %1\n", Properties::inverse_fade_out.property_id)); + Properties::envelope.property_id = g_quark_from_static_string (X_("Envelope")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for Envelope = %1\n", Properties::envelope.property_id)); } void @@ -181,6 +196,11 @@ AudioRegion::register_properties () add_property (_fade_out_is_short); add_property (_fade_in_is_xfade); add_property (_fade_in_is_short); + add_property (_fade_in); + add_property (_inverse_fade_in); + add_property (_fade_out); + add_property (_inverse_fade_out); + add_property (_envelope); } #define AUDIOREGION_STATE_DEFAULT \ @@ -193,7 +213,11 @@ AudioRegion::register_properties () , _fade_in_is_xfade (Properties::fade_in_is_xfade, false) \ , _fade_out_is_xfade (Properties::fade_out_is_xfade, false) \ , _fade_in_is_short (Properties::fade_in_is_short, false) \ - , _fade_out_is_short (Properties::fade_out_is_short, false) + , _fade_out_is_short (Properties::fade_out_is_short, false) \ + , _fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation)))) \ + , _inverse_fade_in (Properties::inverse_fade_in, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation)))) \ + , _fade_out (Properties::fade_out, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeOutAutomation)))) \ + , _inverse_fade_out (Properties::inverse_fade_out, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeOutAutomation)))) #define AUDIOREGION_COPY_STATE(other) \ _envelope_active (Properties::envelope_active, other->_envelope_active) \ @@ -205,7 +229,11 @@ AudioRegion::register_properties () , _fade_in_is_xfade (Properties::fade_in_is_xfade, other->_fade_in_is_xfade) \ , _fade_out_is_xfade (Properties::fade_out_is_xfade, other->_fade_out_is_xfade) \ , _fade_in_is_short (Properties::fade_in_is_short, other->_fade_in_is_short) \ - , _fade_out_is_short (Properties::fade_out_is_short, other->_fade_out_is_short) + , _fade_out_is_short (Properties::fade_out_is_short, other->_fade_out_is_short) \ + , _fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_fade_in.val()))) \ + , _inverse_fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_in.val()))) \ + , _fade_out (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_fade_out.val()))) \ + , _inverse_fade_out (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_out.val()))) /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */ void @@ -227,12 +255,8 @@ AudioRegion::init () AudioRegion::AudioRegion (Session& s, framepos_t start, framecnt_t len, std::string name) : Region (s, start, len, name, DataType::AUDIO) , AUDIOREGION_STATE_DEFAULT + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter(EnvelopeAutomation)))) , _automatable (s) - , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -244,12 +268,8 @@ AudioRegion::AudioRegion (Session& s, framepos_t start, framecnt_t len, std::str AudioRegion::AudioRegion (const SourceList& srcs) : Region (srcs) , AUDIOREGION_STATE_DEFAULT + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter(EnvelopeAutomation)))) , _automatable(srcs[0]->session()) - , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -260,15 +280,11 @@ AudioRegion::AudioRegion (const SourceList& srcs) AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) : Region (other) , AUDIOREGION_COPY_STATE (other) - , _automatable (other->session()) - , _fade_in (new AutomationList (*other->_fade_in)) - , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) - , _fade_out (new AutomationList (*other->_fade_out)) - , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) /* As far as I can see, the _envelope's times are relative to region position, and have nothing to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset. */ - , _envelope (new AutomationList (*other->_envelope, 0, other->_length)) + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val(), 0, other->_length))) + , _automatable (other->session()) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -286,15 +302,11 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, framecnt_t offset) : Region (other, offset) , AUDIOREGION_COPY_STATE (other) - , _automatable (other->session()) - , _fade_in (new AutomationList (*other->_fade_in)) - , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) - , _fade_out (new AutomationList (*other->_fade_out)) - , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) /* As far as I can see, the _envelope's times are relative to region position, and have nothing to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset. */ - , _envelope (new AutomationList (*other->_envelope, offset, other->_length)) + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val(), offset, other->_length))) + , _automatable (other->session()) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -312,12 +324,8 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, framecnt_t AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& srcs) : Region (boost::static_pointer_cast<const Region>(other), srcs) , AUDIOREGION_COPY_STATE (other) + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val()))) , _automatable (other->session()) - , _fade_in (new AutomationList (*other->_fade_in)) - , _inverse_fade_in (new AutomationList(*other->_inverse_fade_in)) - , _fade_out (new AutomationList (*other->_fade_out)) - , _inverse_fade_out (new AutomationList (*other->_inverse_fade_out)) - , _envelope (new AutomationList (*other->_envelope)) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -335,12 +343,8 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const Sour AudioRegion::AudioRegion (SourceList& srcs) : Region (srcs) , AUDIOREGION_STATE_DEFAULT + , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))) , _automatable(srcs[0]->session()) - , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _inverse_fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation))) - , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _inverse_fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation))) - , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) { @@ -988,7 +992,7 @@ void AudioRegion::set_fade_in (boost::shared_ptr<AutomationList> f) { _fade_in->freeze (); - *_fade_in = *f; + *(_fade_in.val()) = *f; _fade_in->thaw (); _default_fade_in = false; @@ -1010,23 +1014,23 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len) case FadeLinear: _fade_in->fast_simple_add (0.0, 0.0); _fade_in->fast_simple_add (len, 1.0); - reverse_curve (_inverse_fade_in, _fade_in); + reverse_curve (_inverse_fade_in.val(), _fade_in.val()); break; case FadeFast: - generate_db_fade (_fade_in, len, 10, -60); - reverse_curve (c1, _fade_in); + generate_db_fade (_fade_in.val(), len, 10, -60); + reverse_curve (c1, _fade_in.val()); _fade_in->copy_events (*c1); - generate_inverse_power_curve (_inverse_fade_in, _fade_in); + generate_inverse_power_curve (_inverse_fade_in.val(), _fade_in.val()); break; case FadeSlow: generate_db_fade (c1, len, 10, -1); // start off with a slow fade generate_db_fade (c2, len, 10, -80); // end with a fast fade - merge_curves (_fade_in, c1, c2); - reverse_curve (c3, _fade_in); + merge_curves (_fade_in.val(), c1, c2); + reverse_curve (c3, _fade_in.val()); _fade_in->copy_events (*c3); - generate_inverse_power_curve (_inverse_fade_in, _fade_in); + generate_inverse_power_curve (_inverse_fade_in.val(), _fade_in.val()); break; case FadeConstantPower: @@ -1035,7 +1039,7 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len) _fade_in->fast_simple_add (len*dist, sin (dist*M_PI/2)); } _fade_in->fast_simple_add (len, 1.0); - reverse_curve (_inverse_fade_in, _fade_in); + reverse_curve (_inverse_fade_in.val(), _fade_in.val()); break; case FadeSymmetric: @@ -1053,9 +1057,9 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len) _fade_in->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff); } _fade_in->fast_simple_add (len, VERY_SMALL_SIGNAL); - reverse_curve (c3, _fade_in); + reverse_curve (c3, _fade_in.val()); _fade_in->copy_events (*c3); - reverse_curve (_inverse_fade_in, _fade_in ); + reverse_curve (_inverse_fade_in.val(), _fade_in.val()); break; } @@ -1068,7 +1072,7 @@ void AudioRegion::set_fade_out (boost::shared_ptr<AutomationList> f) { _fade_out->freeze (); - *_fade_out = *f; + *(_fade_out.val()) = *f; _fade_out->thaw (); _default_fade_out = false; @@ -1089,19 +1093,19 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len) case FadeLinear: _fade_out->fast_simple_add (0.0, 1.0); _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL); - reverse_curve (_inverse_fade_out, _fade_out); + reverse_curve (_inverse_fade_out.val(), _fade_out.val()); break; case FadeFast: - generate_db_fade (_fade_out, len, 10, -60); - generate_inverse_power_curve (_inverse_fade_out, _fade_out); + generate_db_fade (_fade_out.val(), len, 10, -60); + generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val()); break; case FadeSlow: generate_db_fade (c1, len, 10, -1); //start off with a slow fade generate_db_fade (c2, len, 10, -80); //end with a fast fade - merge_curves (_fade_out, c1, c2); - generate_inverse_power_curve (_inverse_fade_out, _fade_out); + merge_curves (_fade_out.val(), c1, c2); + generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val()); break; case FadeConstantPower: @@ -1113,7 +1117,7 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len) _fade_out->fast_simple_add ((len * dist), cos(dist*M_PI/2)); } _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL); - reverse_curve (_inverse_fade_out, _fade_out); + reverse_curve (_inverse_fade_out.val(), _fade_out.val()); break; case FadeSymmetric: @@ -1132,7 +1136,7 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len) _fade_out->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff); } _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL); - reverse_curve (_inverse_fade_out, _fade_out); + reverse_curve (_inverse_fade_out.val(), _fade_out.val()); break; } diff --git a/libs/ardour/automation_list.cc b/libs/ardour/automation_list.cc index 54d838f6e2..26ca7b097a 100644 --- a/libs/ardour/automation_list.cc +++ b/libs/ardour/automation_list.cc @@ -510,3 +510,24 @@ AutomationList::set_state (const XMLNode& node, int version) return 0; } +bool +AutomationList::operator!= (AutomationList const & other) const +{ + return ( + static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) || + _state != other._state || + _style != other._style || + _touching != other._touching + ); +} + +PBD::PropertyBase * +AutomationListProperty::clone () const +{ + return new AutomationListProperty ( + this->property_id(), + boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())), + boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get())) + ); +} + diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 35aef9dc86..1c6b98155c 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -115,34 +115,6 @@ extern void setup_enum_writer (); */ PBD::PropertyChange ARDOUR::bounds_change; -namespace ARDOUR { - namespace Properties { - - /* the envelope and fades are not scalar items and so - currently (2010/02) are not stored using Property. - However, these descriptors enable us to notify - about changes to them via PropertyChange. - - Declared in ardour/audioregion.h ... - */ - - PBD::PropertyDescriptor<bool> fade_in; - PBD::PropertyDescriptor<bool> fade_out; - PBD::PropertyDescriptor<bool> envelope; - } -} - -void -ARDOUR::make_property_quarks () -{ - Properties::fade_in.property_id = g_quark_from_static_string (X_("fade_in_FAKE")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade_in_FAKE = %1\n", Properties::fade_in.property_id)); - Properties::fade_out.property_id = g_quark_from_static_string (X_("fade_out_FAKE")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade_out_FAKE = %1\n", Properties::fade_out.property_id)); - Properties::envelope.property_id = g_quark_from_static_string (X_("envelope_FAKE")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for envelope_FAKE = %1\n", Properties::envelope.property_id)); -} - void setup_hardware_optimization (bool try_optimization) { @@ -248,7 +220,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization) PBD::ID::init (); SessionEvent::init_event_pool (); - make_property_quarks (); SessionObject::make_property_quarks (); Region::make_property_quarks (); MidiRegion::make_property_quarks (); diff --git a/libs/ardour/test/automation_list_property_test.cc b/libs/ardour/test/automation_list_property_test.cc new file mode 100644 index 0000000000..9d1f448ad5 --- /dev/null +++ b/libs/ardour/test/automation_list_property_test.cc @@ -0,0 +1,123 @@ +/* + Copyright (C) 2012 Paul Davis + + 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. +*/ + +#include "pbd/properties.h" +#include "pbd/stateful_diff_command.h" +#include "ardour/automation_list.h" +#include "automation_list_property_test.h" +#include "test_util.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (AutomationListPropertyTest); + +using namespace std; +using namespace PBD; +using namespace ARDOUR; + +void +AutomationListPropertyTest::basicTest () +{ + PropertyDescriptor<boost::shared_ptr<AutomationList> > descriptor; + descriptor.property_id = g_quark_from_static_string ("FadeIn"); + AutomationListProperty property ( + descriptor, + boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation))) + ); + + property.clear_changes (); + + /* No change since we just cleared them */ + CPPUNIT_ASSERT_EQUAL (false, property.changed()); + + property->add (1, 2); + property->add (3, 4); + + /* Now it has changed */ + CPPUNIT_ASSERT_EQUAL (true, property.changed()); + + XMLNode* foo = new XMLNode ("test"); + property.get_changes_as_xml (foo); + check_xml (foo, "../libs/ardour/test/data/automation_list_property_test1.ref"); + + /* Do some more */ + property.clear_changes (); + CPPUNIT_ASSERT_EQUAL (false, property.changed()); + property->add (5, 6); + property->add (7, 8); + CPPUNIT_ASSERT_EQUAL (true, property.changed()); + foo = new XMLNode ("test"); + property.get_changes_as_xml (foo); + check_xml (foo, "../libs/ardour/test/data/automation_list_property_test2.ref"); +} + +/** Here's a StatefulDestructible class that has a AutomationListProperty */ +class Fred : public StatefulDestructible +{ +public: + Fred () + : _jim (_descriptor, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation)))) + + { + add_property (_jim); + } + + XMLNode & get_state () { + XMLNode* n = new XMLNode ("State"); + add_properties (*n); + return *n; + } + + int set_state (XMLNode const & node, int) { + set_values (node); + return 0; + } + + static void make_property_quarks () { + _descriptor.property_id = g_quark_from_static_string ("FadeIn"); + } + + AutomationListProperty _jim; + static PropertyDescriptor<boost::shared_ptr<AutomationList> > _descriptor; +}; + +PropertyDescriptor<boost::shared_ptr<AutomationList> > Fred::_descriptor; + +void +AutomationListPropertyTest::undoTest () +{ + Fred::make_property_quarks (); + + boost::shared_ptr<Fred> sheila (new Fred); + + /* Add some data */ + sheila->_jim->add (1, 2); + sheila->_jim->add (3, 4); + + /* Do a `command' */ + sheila->clear_changes (); + sheila->_jim->add (5, 6); + sheila->_jim->add (7, 8); + StatefulDiffCommand sdc (sheila); + + /* Undo */ + sdc.undo (); + check_xml (&sheila->get_state(), "../libs/ardour/test/data/automation_list_property_test3.ref"); + + /* Redo */ + sdc.redo (); + check_xml (&sheila->get_state(), "../libs/ardour/test/data/automation_list_property_test4.ref"); +} diff --git a/libs/ardour/test/automation_list_property_test.h b/libs/ardour/test/automation_list_property_test.h new file mode 100644 index 0000000000..8885d23242 --- /dev/null +++ b/libs/ardour/test/automation_list_property_test.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2012 Paul Davis + + 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. +*/ + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +class AutomationListPropertyTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (AutomationListPropertyTest); + CPPUNIT_TEST (basicTest); + CPPUNIT_TEST (undoTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void basicTest (); + void undoTest (); +}; diff --git a/libs/ardour/test/data/automation_list_property_test1.ref b/libs/ardour/test/data/automation_list_property_test1.ref new file mode 100644 index 0000000000..b7c11f153a --- /dev/null +++ b/libs/ardour/test/data/automation_list_property_test1.ref @@ -0,0 +1,17 @@ +<test> + <FadeIn> + <from> + <AutomationList automation-id="fadein" id="162" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + </AutomationList> + </from> + <to> + <AutomationList automation-id="fadein" id="162" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + <events> + 1 2 +3 4 + + </events> + </AutomationList> + </to> + </FadeIn> +</test> diff --git a/libs/ardour/test/data/automation_list_property_test2.ref b/libs/ardour/test/data/automation_list_property_test2.ref new file mode 100644 index 0000000000..17df0e4f54 --- /dev/null +++ b/libs/ardour/test/data/automation_list_property_test2.ref @@ -0,0 +1,24 @@ +<test> + <FadeIn> + <from> + <AutomationList automation-id="fadein" id="162" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + <events> + 1 2 +3 4 + + </events> + </AutomationList> + </from> + <to> + <AutomationList automation-id="fadein" id="162" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + <events> + 1 2 +3 4 +5 6 +7 8 + + </events> + </AutomationList> + </to> + </FadeIn> +</test> diff --git a/libs/ardour/test/data/automation_list_property_test3.ref b/libs/ardour/test/data/automation_list_property_test3.ref new file mode 100644 index 0000000000..edc1469d10 --- /dev/null +++ b/libs/ardour/test/data/automation_list_property_test3.ref @@ -0,0 +1,11 @@ +<State> + <FadeIn> + <AutomationList automation-id="fadein" id="166" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + <events> + 1 2 +3 4 + + </events> + </AutomationList> + </FadeIn> +</State> diff --git a/libs/ardour/test/data/automation_list_property_test4.ref b/libs/ardour/test/data/automation_list_property_test4.ref new file mode 100644 index 0000000000..5a6024a429 --- /dev/null +++ b/libs/ardour/test/data/automation_list_property_test4.ref @@ -0,0 +1,13 @@ +<State> + <FadeIn> + <AutomationList automation-id="fadein" id="166" default="0" min-yval="0" max-yval="2" interpolation-style="Linear" state="Off" style="Absolute"> + <events> + 1 2 +3 4 +5 6 +7 8 + + </events> + </AutomationList> + </FadeIn> +</State> diff --git a/libs/ardour/test/test_util.cc b/libs/ardour/test/test_util.cc new file mode 100644 index 0000000000..f1237f72df --- /dev/null +++ b/libs/ardour/test/test_util.cc @@ -0,0 +1,20 @@ +#include <fstream> +#include <sstream> +#include "pbd/xml++.h" +#include <cppunit/extensions/HelperMacros.h> + +using namespace std; + +void +check_xml (XMLNode* node, string ref_file) +{ + system ("rm -f libs/ardour/test/test.xml"); + ofstream f ("libs/ardour/test/test.xml"); + node->dump (f); + f.close (); + + stringstream cmd; + cmd << "diff -u libs/ardour/test/test.xml " << ref_file; + CPPUNIT_ASSERT_EQUAL (0, system (cmd.str().c_str ())); +} + diff --git a/libs/ardour/test/test_util.h b/libs/ardour/test/test_util.h new file mode 100644 index 0000000000..84766dd3a1 --- /dev/null +++ b/libs/ardour/test/test_util.h @@ -0,0 +1,2 @@ + +extern void check_xml (XMLNode *, std::string); diff --git a/libs/ardour/wscript b/libs/ardour/wscript index c16c0469ec..2eb279b535 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -424,10 +424,12 @@ def build(bld): testobj = bld(features = 'cxx cxxprogram') testobj.source = ''' test/dummy_lxvst.cc + test/test_util.cc test/test_needing_session.cc test/audio_region_test.cc test/test_globals.cc test/audio_region_read_test.cc + test/automation_list_property_test.cc test/bbt_test.cc test/tempo_test.cc test/interpolation_test.cc diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 40e2e54625..b1ec7897cf 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -255,6 +255,8 @@ public: static void set_thinning_factor (double d); static double thinning_factor() { return _thinning_factor; } + bool operator!= (ControlList const &) const; + protected: /** Called by unlocked_eval() to handle cases of 3 or more control points. */ diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 9a820294ef..53200398a5 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -1529,5 +1529,33 @@ ControlList::set_thinning_factor (double v) _thinning_factor = v; } +bool +ControlList::operator!= (ControlList const & other) const +{ + if (_events.size() != other._events.size()) { + return true; + } + + EventList::const_iterator i = _events.begin (); + EventList::const_iterator j = other._events.begin (); + + while (i != _events.end() && (*i)->when == (*j)->when && (*i)->value == (*j)->value) { + ++i; + ++j; + } + + if (i != _events.end ()) { + return true; + } + + return ( + _parameter != other._parameter || + _interpolation != other._interpolation || + _min_yval != other._min_yval || + _max_yval != other._max_yval || + _default_value != other._default_value + ); +} + } // namespace Evoral diff --git a/libs/pbd/pbd/properties.h b/libs/pbd/pbd/properties.h index d961046760..e65929c60c 100644 --- a/libs/pbd/pbd/properties.h +++ b/libs/pbd/pbd/properties.h @@ -30,6 +30,7 @@ #include "pbd/property_basics.h" #include "pbd/property_list.h" #include "pbd/enumwriter.h" +#include "pbd/stateful.h" namespace PBD { @@ -341,7 +342,121 @@ private: /* no copy-construction */ EnumProperty (EnumProperty const &); }; + +/** A Property which holds a shared_ptr to a Stateful object, + * and handles undo using the somewhat inefficient approach + * of saving the complete XML state of its object before and + * after changes. A sort of half-way house between the old + * complete-state undo system and the new difference-based + * one. + */ +template <class T> +class SharedStatefulProperty : public PropertyBase +{ +public: + typedef boost::shared_ptr<T> Ptr; + SharedStatefulProperty (PropertyID d, Ptr p) + : PropertyBase (d) + , _current (p) + { + + } + + SharedStatefulProperty (PropertyID d, Ptr o, Ptr c) + : PropertyBase (d) + , _old (o) + , _current (c) + { + + } + + bool set_value (XMLNode const & node) { + + /* Look for our node */ + XMLNode* n = node.child (property_name ()); + if (!n) { + return false; + } + + /* And there should be one child which is the state of our T */ + XMLNodeList const & children = n->children (); + if (children.size() != 1) { + return false; + } + + _current->set_state (*children.front (), Stateful::current_state_version); + return true; + } + + void get_value (XMLNode & node) const { + XMLNode* n = node.add_child (property_name ()); + n->add_child_nocopy (_current->get_state ()); + } + + void clear_changes () { + /* We are starting to change things, so _old gets set up + with the current state. + */ + _old.reset (new T (*_current.get())); + } + + bool changed () const { + /* Expensive, but, hey; this requires operator!= in + our T + */ + return (*_old != *_current); + } + + void invert () { + _current.swap (_old); + } + + void get_changes_as_xml (XMLNode* history_node) const { + /* We express the diff as before and after state, just + as MementoCommand does. + */ + XMLNode* p = history_node->add_child (property_name ()); + XMLNode* from = p->add_child ("from"); + from->add_child_nocopy (_old->get_state ()); + XMLNode* to = p->add_child ("to"); + to->add_child_nocopy (_current->get_state ()); + } + + void get_changes_as_properties (PropertyList& changes, Command *) const { + if (changed ()) { + changes.add (clone ()); + } + } + + void apply_changes (PropertyBase const * p) { + *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val (); + } + + Ptr val () const { + return _current; + } + + T* operator-> () const { + return _current.operator-> (); + } + + operator bool () const { + return _current; + } + +protected: + + Ptr _old; + Ptr _current; + +private: + + /* No copy-construction nor assignment */ + SharedStatefulProperty (SharedStatefulProperty<T> const &); + SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &); +}; + } /* namespace PBD */ #include "pbd/property_list_impl.h" diff --git a/libs/pbd/xml++.cc b/libs/pbd/xml++.cc index e2ccd67738..3046f971ab 100644 --- a/libs/pbd/xml++.cc +++ b/libs/pbd/xml++.cc @@ -652,13 +652,19 @@ static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath) void XMLNode::dump (ostream& s, string p) const { - s << p << _name << " "; - for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) { - s << (*i)->name() << "=" << (*i)->value() << " "; - } - s << "\n"; - - for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) { - (*i)->dump (s, p + " "); + if (_is_content) { + s << p << " " << content() << "\n"; + } else { + s << p << "<" << _name; + for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) { + s << " " << (*i)->name() << "=\"" << (*i)->value() << "\""; + } + s << ">\n"; + + for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) { + (*i)->dump (s, p + " "); + } + + s << p << "</" << _name << ">\n"; } } |