13
0

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
This commit is contained in:
Carl Hetherington 2012-06-16 17:20:10 +00:00
parent 9429401f11
commit 5ac22e9095
18 changed files with 524 additions and 113 deletions

View File

@ -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;

View File

@ -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&);

View File

@ -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;
}

View File

@ -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()))
);
}

View File

@ -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 ();

View File

@ -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");
}

View File

@ -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 ();
};

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 ()));
}

View File

@ -0,0 +1,2 @@
extern void check_xml (XMLNode *, std::string);

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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"

View File

@ -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";
}
}