X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplaylist.cc;h=6fc7fdc2cffa9d92020485a07aa77d672b850647;hb=d9cebc2edf8accded23c6f2a1caab73eb47a50fd;hp=e128489820b3f150b9fdff8fbd58fedbb14a9a9b;hpb=650c6d5824222a8879df5c5ba9645c264ed3b84f;p=ardour.git diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index e128489820..6fc7fdc2cf 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -25,11 +25,11 @@ #include #include +#include #include "pbd/failed_constructor.h" -#include "pbd/stl_delete.h" +#include "pbd/stateful_diff_command.h" #include "pbd/xml++.h" -#include "pbd/stacktrace.h" #include "ardour/debug.h" #include "ardour/playlist.h" @@ -46,6 +46,12 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +namespace ARDOUR { +namespace Properties { +PBD::PropertyDescriptor regions; +} +} + struct ShowMeTheList { ShowMeTheList (boost::shared_ptr pl, const string& n) : playlist (pl), name (n) {} ~ShowMeTheList () { @@ -90,32 +96,87 @@ struct RegionSortByLastLayerOp { } }; +void +Playlist::make_property_quarks () +{ + Properties::regions.property_id = g_quark_from_static_string (X_("regions")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id)); +} + +RegionListProperty::RegionListProperty (Playlist& pl) + : SequenceProperty > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1)) + , _playlist (pl) +{ +} + +boost::shared_ptr +RegionListProperty::lookup_id (const ID& id) +{ + boost::shared_ptr ret = _playlist.region_by_id (id); + + if (!ret) { + ret = RegionFactory::region_by_id (id); + } + + return ret; +} + +RegionListProperty* +RegionListProperty::copy_for_history () const +{ + RegionListProperty* copy = new RegionListProperty (_playlist); + /* this is all we need */ + copy->_change = _change; + return copy; +} + +void +RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const +{ + if (changed()) { + /* list of the removed/added regions since clear_history() was last called */ + RegionListProperty* a = copy_for_history (); + + /* the same list, but with removed/added lists swapped (for undo purposes) */ + RegionListProperty* b = copy_for_history (); + b->invert_changes (); + + undo.add (b); + redo.add (a); + } +} Playlist::Playlist (Session& sess, string nom, DataType type, bool hide) : SessionObject(sess, nom) + , regions (*this) , _type(type) { init (hide); first_set_state = false; _name = nom; + _set_sort_id (); } Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide) : SessionObject(sess, "unnamed playlist") - , _type(type) + , regions (*this) + , _type(type) + { const XMLProperty* prop = node.property("type"); assert(!prop || DataType(prop->value()) == _type); init (hide); _name = "unnamed"; /* reset by set_state */ + _set_sort_id (); /* set state called by derived class */ } Playlist::Playlist (boost::shared_ptr other, string namestr, bool hide) : SessionObject(other->_session, namestr) + , regions (*this) , _type(other->_type) , _orig_diskstream_id(other->_orig_diskstream_id) { @@ -150,6 +211,7 @@ Playlist::Playlist (boost::shared_ptr other, string namestr, boo Playlist::Playlist (boost::shared_ptr other, framepos_t start, framecnt_t cnt, string str, bool hide) : SessionObject(other->_session, str) + , regions (*this) , _type(other->_type) , _orig_diskstream_id(other->_orig_diskstream_id) { @@ -204,11 +266,11 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f break; } - _session.region_name (new_name, region->name(), false); + RegionFactory::region_name (new_name, region->name(), false); PropertyList plist; - plist.add (Properties::start, offset); + plist.add (Properties::start, region->start() + offset); plist.add (Properties::length, len); plist.add (Properties::name, new_name); plist.add (Properties::layer, region->layer()); @@ -256,6 +318,9 @@ Playlist::copy_regions (RegionList& newlist) const void Playlist::init (bool hide) { + add_property (regions); + _xml_node_name = X_("Playlist"); + g_atomic_int_set (&block_notifications, 0); g_atomic_int_set (&ignore_state_changes, 0); pending_contents_change = false; @@ -268,6 +333,7 @@ Playlist::init (bool hide) _shuffling = false; _nudging = false; in_set_state = 0; + in_update = false; _edit_mode = Config->get_edit_mode(); in_flush = false; in_partition = false; @@ -299,6 +365,32 @@ Playlist::~Playlist () /* GoingAway must be emitted by derived classes */ } +void +Playlist::_set_sort_id () +{ + /* + Playlists are given names like . + or .. where id + is an integer. We extract the id and sort by that. + */ + + size_t dot_position = _name.val().find_last_of("."); + + if (dot_position == string::npos) { + _sort_id = 0; + } else { + string t = _name.val().substr(dot_position + 1); + + try { + _sort_id = boost::lexical_cast(t); + } + + catch (boost::bad_lexical_cast e) { + _sort_id = 0; + } + } +} + bool Playlist::set_name (const string& str) { @@ -310,9 +402,13 @@ Playlist::set_name (const string& str) if (_refcnt > 2) { return false; - } else { - return SessionObject::set_name(str); - } + } + + bool ret = SessionObject::set_name(str); + if (ret) { + _set_sort_id (); + } + return ret; } /*********************************************************************** @@ -326,6 +422,7 @@ Playlist::set_name (const string& str) void Playlist::begin_undo () { + in_update = true; freeze (); } @@ -333,6 +430,7 @@ void Playlist::end_undo () { thaw (); + in_update = false; } void @@ -362,7 +460,8 @@ Playlist::release_notifications () { if (g_atomic_int_dec_and_test (&block_notifications)) { flush_notifications (); - } + } + } void @@ -430,7 +529,7 @@ Playlist::notify_region_added (boost::shared_ptr r) /* the length change might not be true, but we have to act as though it could be. */ - + if (holding_state()) { pending_adds.insert (r); pending_contents_change = true; @@ -596,12 +695,12 @@ Playlist::add_region (boost::shared_ptr region, framepos_t position, flo if (floor (times) != times) { length = (framecnt_t) floor (region->length() * (times - floor (times))); string name; - _session.region_name (name, region->name(), false); + RegionFactory::region_name (name, region->name(), false); { PropertyList plist; - plist.add (Properties::start, 0); + plist.add (Properties::start, region->start()); plist.add (Properties::length, length); plist.add (Properties::name, name); plist.add (Properties::layer, region->layer()); @@ -655,8 +754,8 @@ Playlist::add_region_internal (boost::shared_ptr region, framepos_t posi possibly_splice_unlocked (position, region->length(), region); - if (!holding_state () && !in_set_state) { - /* layers get assigned from XML state */ + if (!holding_state ()) { + /* layers get assigned from XML state, and are not reset during undo/redo */ relayer (); } @@ -730,7 +829,7 @@ Playlist::remove_region_internal (boost::shared_ptr region) possibly_splice_unlocked (pos, -distance); if (!holding_state ()) { - relayer (); + relayer (); remove_dependents (region); if (old_length != _get_maximum_extent()) { @@ -786,7 +885,7 @@ Playlist::partition (framepos_t start, framepos_t end, bool cut) partition_internal (start, end, cut, thawlist); for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { - (*i)->thaw (); + (*i)->resume_property_changes (); } } @@ -811,7 +910,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re get operated on as well. */ - RegionList copy = regions; + RegionList copy = regions.rlist(); for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) { @@ -866,11 +965,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re if (!cutting) { /* "middle" ++++++ */ - _session.region_name (new_name, current->name(), false); + RegionFactory::region_name (new_name, current->name(), false); PropertyList plist; - plist.add (Properties::start, pos2 - pos1); + plist.add (Properties::start, current->start() + (pos2 - pos1)); plist.add (Properties::length, pos3 - pos2); plist.add (Properties::name, new_name); plist.add (Properties::layer, regions.size()); @@ -885,11 +984,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re /* "end" ====== */ - _session.region_name (new_name, current->name(), false); + RegionFactory::region_name (new_name, current->name(), false); PropertyList plist; - plist.add (Properties::start, pos3 - pos1); + plist.add (Properties::start, current->start() + (pos3 - pos1)); plist.add (Properties::length, pos4 - pos3); plist.add (Properties::name, new_name); plist.add (Properties::layer, regions.size()); @@ -903,9 +1002,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re /* "front" ***** */ - current->freeze (); + current->suspend_property_changes (); thawlist.push_back (current); - current->trim_end (pos2, this); + current->cut_end (pos2 - 1, this); } else if (overlap == OverlapEnd) { @@ -923,11 +1022,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re /* end +++++ */ - _session.region_name (new_name, current->name(), false); + RegionFactory::region_name (new_name, current->name(), false); PropertyList plist; - plist.add (Properties::start, pos2 - pos1); + plist.add (Properties::start, current->start() + (pos2 - pos1)); plist.add (Properties::length, pos4 - pos2); plist.add (Properties::name, new_name); plist.add (Properties::layer, regions.size()); @@ -942,9 +1041,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re /* front ****** */ - current->freeze (); + current->suspend_property_changes (); thawlist.push_back (current); - current->trim_end (pos2, this); + current->cut_end (pos2 - 1, this); } else if (overlap == OverlapStart) { @@ -966,11 +1065,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re if (!cutting) { /* front **** */ - _session.region_name (new_name, current->name(), false); + RegionFactory::region_name (new_name, current->name(), false); PropertyList plist; - plist.add (Properties::start, 0); + plist.add (Properties::start, current->start()); plist.add (Properties::length, pos3 - pos1); plist.add (Properties::name, new_name); plist.add (Properties::layer, regions.size()); @@ -985,7 +1084,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re /* end */ - current->freeze (); + current->suspend_property_changes (); thawlist.push_back (current); current->trim_front (pos3, this); } else if (overlap == OverlapExternal) { @@ -1088,7 +1187,7 @@ Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden) partition_internal (start, start+cnt-1, true, thawlist); for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { - (*i)->thaw (); + (*i)->resume_property_changes(); } return the_copy; @@ -1170,12 +1269,12 @@ Playlist::duplicate (boost::shared_ptr region, framepos_t position, floa if (floor (times) != times) { framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times))); string name; - _session.region_name (name, region->name(), false); + RegionFactory::region_name (name, region->name(), false); { PropertyList plist; - plist.add (Properties::start, 0); + plist.add (Properties::start, region->start()); plist.add (Properties::length, length); plist.add (Properties::name, name); @@ -1189,7 +1288,7 @@ void Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue) { RegionLock rlock (this); - RegionList copy (regions); + RegionList copy (regions.rlist()); RegionList fixup; for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { @@ -1227,7 +1326,7 @@ void Playlist::split (framepos_t at) { RegionLock rlock (this); - RegionList copy (regions); + RegionList copy (regions.rlist()); /* use a copy since this operation can modify the region list */ @@ -1271,12 +1370,12 @@ Playlist::_split_region (boost::shared_ptr region, framepos_t playlist_p before = playlist_position - region->position(); after = region->length() - before; - _session.region_name (before_name, region->name(), false); + RegionFactory::region_name (before_name, region->name(), false); { PropertyList plist; - plist.add (Properties::start, 0); + plist.add (Properties::start, region->start()); plist.add (Properties::length, before); plist.add (Properties::name, before_name); plist.add (Properties::left_of_split, true); @@ -1284,12 +1383,12 @@ Playlist::_split_region (boost::shared_ptr region, framepos_t playlist_p left = RegionFactory::create (region, plist); } - _session.region_name (after_name, region->name(), false); + RegionFactory::region_name (after_name, region->name(), false); { PropertyList plist; - plist.add (Properties::start, before); + plist.add (Properties::start, region->start() + before); plist.add (Properties::length, after); plist.add (Properties::name, after_name); plist.add (Properties::right_of_split, true); @@ -1406,9 +1505,10 @@ Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shar RegionList::iterator i = find (regions.begin(), regions.end(), region); if (i == regions.end()) { - warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"), - _name, region->name()) - << endmsg; + /* the region bounds are being modified but its not currently + in the region list. we will use its bounds correctly when/if + it is added + */ return; } @@ -1530,9 +1630,19 @@ Playlist::clear (bool with_signals) } regions.clear (); + + for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { + remove_dependents (*s); + } } if (with_signals) { + + for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { + RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ + } + + pending_removes.clear (); pending_length = false; LengthChanged (); pending_contents_change = false; @@ -1939,6 +2049,85 @@ Playlist::mark_session_dirty () } } +bool +Playlist::set_property (const PropertyBase& prop) +{ + if (prop == Properties::regions.property_id) { + const RegionListProperty::ChangeRecord& change (dynamic_cast(&prop)->change()); + regions.update (change); + return (!change.added.empty() && !change.removed.empty()); + } + return false; +} + +void +Playlist::rdiff (vector& cmds) const +{ + RegionLock rlock (const_cast (this)); + + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->changed ()) { + StatefulDiffCommand* sdc = new StatefulDiffCommand (*i); + cmds.push_back (sdc); + } + } +} + +void +Playlist::clear_owned_history () +{ + RegionLock rlock (this); + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + (*i)->clear_history (); + } +} + +void +Playlist::update (const RegionListProperty::ChangeRecord& change) +{ + DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n", + name(), change.added.size(), change.removed.size())); + + freeze (); + /* add the added regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) { + add_region ((*i), (*i)->position()); + } + /* remove the removed regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) { + remove_region (*i); + } + + thaw (); +} + +PropertyList* +Playlist::property_factory (const XMLNode& history_node) const +{ + const XMLNodeList& children (history_node.children()); + PropertyList* prop_list = 0; + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == capitalize (regions.property_name())) { + + RegionListProperty* rlp = new RegionListProperty (*const_cast (this)); + + if (rlp->load_history_state (**i)) { + if (!prop_list) { + prop_list = new PropertyList(); + } + prop_list->add (rlp); + } else { + delete rlp; + } + } + } + + return prop_list; +} + int Playlist::set_state (const XMLNode& node, int version) { @@ -1968,6 +2157,9 @@ Playlist::set_state (const XMLNode& node, int version) if (prop->name() == X_("name")) { _name = prop->value(); + _set_sort_id (); + } else if (prop->name() == X_("id")) { + _id = prop->value(); } else if (prop->name() == X_("orig_diskstream_id")) { _orig_diskstream_id = prop->value (); } else if (prop->name() == X_("frozen")) { @@ -1975,7 +2167,7 @@ Playlist::set_state (const XMLNode& node, int version) } } - clear (false); + clear (true); nlist = node.children(); @@ -1994,25 +2186,25 @@ Playlist::set_state (const XMLNode& node, int version) if ((region = region_by_id (id))) { - region->freeze (); + region->suspend_property_changes (); if (region->set_state (*child, version)) { - region->thaw (); + region->resume_property_changes (); continue; } } else if ((region = RegionFactory::create (_session, *child, true)) != 0) { - region->freeze (); + region->suspend_property_changes (); } else { error << _("Playlist: cannot create region from XML") << endmsg; continue; } - + add_region (region, region->position(), 1.0); // So that layer_op ordering doesn't get screwed up region->set_last_layer_op( region->layer()); - region->thaw (); + region->resume_property_changes (); } } @@ -2024,7 +2216,6 @@ Playlist::set_state (const XMLNode& node, int version) check_dependents (*r, false); } - clear_pending (); // this makes thaw() do nothing thaw (); notify_contents_changed (); @@ -2053,6 +2244,7 @@ Playlist::state (bool full_state) XMLNode *node = new XMLNode (X_("Playlist")); char buf[64]; + node->add_property (X_("id"), id().to_s()); node->add_property (X_("name"), _name); node->add_property (X_("type"), _type.to_string()); @@ -2149,6 +2341,12 @@ Playlist::set_edit_mode (EditMode mode) void Playlist::relayer () { + /* never compute layers when changing state for undo/redo or setting from XML */ + + if (in_update || in_set_state) { + return; + } + bool changed = false; /* Build up a new list of regions on each layer, stored in a set of lists @@ -2177,7 +2375,7 @@ Playlist::relayer () which depends on the layer model */ - RegionList copy = regions; + RegionList copy = regions.rlist(); /* sort according to the model and the layering mode that we're in */ @@ -2191,6 +2389,7 @@ Playlist::relayer () } + for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { /* reset the pending explicit relayer flag for every region, now that we're relayering */ @@ -2291,22 +2490,48 @@ void Playlist::raise_region_to_top (boost::shared_ptr region) { /* does nothing useful if layering mode is later=higher */ - if ((_session.config.get_layer_model() == MoveAddHigher) || - (_session.config.get_layer_model() == AddHigher)) { - timestamp_layer_op (region); - relayer (); + switch (_session.config.get_layer_model()) { + case LaterHigher: + return; + default: + break; + } + + layer_t top = regions.size() - 1; + + if (region->layer() >= top) { + /* already on the top */ + return; } + + move_region_to_layer (top, region, 1); + /* mark the region's last_layer_op as now, so that it remains on top when + doing future relayers (until something else takes over) + */ + timestamp_layer_op (region); } void Playlist::lower_region_to_bottom (boost::shared_ptr region) { /* does nothing useful if layering mode is later=higher */ - if ((_session.config.get_layer_model() == MoveAddHigher) || - (_session.config.get_layer_model() == AddHigher)) { - region->set_last_layer_op (0); - relayer (); + switch (_session.config.get_layer_model()) { + case LaterHigher: + return; + default: + break; } + + if (region->layer() == 0) { + /* already on the bottom */ + return; + } + + move_region_to_layer (0, region, -1); + /* force region's last layer op to zero so that it stays at the bottom + when doing future relayers + */ + region->set_last_layer_op (0); } int @@ -2447,7 +2672,7 @@ Playlist::find_region (const ID& id) const } boost::shared_ptr -Playlist::region_by_id (ID id) +Playlist::region_by_id (const ID& id) { /* searches all regions ever added to this playlist */ @@ -2489,8 +2714,6 @@ Playlist::set_frozen (bool yn) void Playlist::timestamp_layer_op (boost::shared_ptr region) { -// struct timeval tv; -// gettimeofday (&tv, 0); region->set_last_layer_op (++layer_op_counter); } @@ -2627,7 +2850,7 @@ void Playlist::update_after_tempo_map_change () { RegionLock rlock (const_cast (this)); - RegionList copy (regions); + RegionList copy (regions.rlist()); freeze ();