X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplaylist.cc;h=cc9dc62ec79c027fcc3f8417c11ab7821bb5d190;hb=89b686f9375decea756e4111a7c1f44192e00f0b;hp=f3a9c9949779a569cea0370a396362fa227fd128;hpb=fa701b8c065251d242342b86a54d91826d2290a0;p=ardour.git diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index f3a9c99497..cc9dc62ec7 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -17,6 +17,9 @@ */ +#define __STDC_LIMIT_MACROS +#include + #include #include #include @@ -25,11 +28,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 +49,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,34 +99,103 @@ 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, Command* cmd) 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 (); + + if (cmd) { + /* whenever one of the regions emits DropReferences, make sure + that the Destructible we've been told to notify hears about + it. the Destructible is likely to be the Command being built + with this diff(). + */ + + for (set >::iterator i = a->change().added.begin(); i != a->change().added.end(); ++i) { + (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd)); + } + } + + 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) + { +#ifndef NDEBUG const XMLProperty* prop = node.property("type"); assert(!prop || DataType(prop->value()) == _type); +#endif 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) + , _orig_diskstream_id (other->_orig_diskstream_id) { init (hide); @@ -150,8 +228,9 @@ 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) + , _orig_diskstream_id (other->_orig_diskstream_id) { RegionLock rlock2 (const_cast (other.get())); @@ -161,7 +240,7 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f in_set_state++; - for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) { + for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) { boost::shared_ptr region; boost::shared_ptr new_region; @@ -204,11 +283,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 +335,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 +350,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; @@ -278,6 +361,9 @@ Playlist::init (bool hide) freeze_length = 0; _explicit_relayering = false; + _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this)); + _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this)); + ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this)); } @@ -296,6 +382,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) { @@ -307,9 +419,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; } /*********************************************************************** @@ -320,6 +436,20 @@ Playlist::set_name (const string& str) the lock (e.g. to read from the playlist). ***********************************************************************/ +void +Playlist::begin_undo () +{ + in_update = true; + freeze (); +} + +void +Playlist::end_undo () +{ + thaw (); + in_update = false; +} + void Playlist::freeze () { @@ -339,7 +469,7 @@ void Playlist::delay_notifications () { g_atomic_int_inc (&block_notifications); - freeze_length = _get_maximum_extent(); + freeze_length = _get_extent().second; } void @@ -347,7 +477,8 @@ Playlist::release_notifications () { if (g_atomic_int_dec_and_test (&block_notifications)) { flush_notifications (); - } + } + } void @@ -357,9 +488,7 @@ Playlist::notify_contents_changed () pending_contents_change = true; } else { pending_contents_change = false; - cerr << _name << "send contents change @ " << get_microseconds() << endl; ContentsChanged(); /* EMIT SIGNAL */ - cerr << _name << "done with cc @ " << get_microseconds() << endl; } } @@ -370,9 +499,7 @@ Playlist::notify_layering_changed () pending_layering = true; } else { pending_layering = false; - cerr << _name << "send layering @ " << get_microseconds() << endl; LayeringChanged(); /* EMIT SIGNAL */ - cerr << _name << "done with layering @ " << get_microseconds() << endl; } } @@ -419,19 +546,18 @@ 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; pending_length = true; } else { + r->clear_history (); pending_length = false; LengthChanged (); /* EMIT SIGNAL */ pending_contents_change = false; RegionAdded (boost::weak_ptr (r)); /* EMIT SIGNAL */ - cerr << _name << "send3 contents changed @ " << get_microseconds() << endl; ContentsChanged (); /* EMIT SIGNAL */ - cerr << _name << "done contents changed @ " << get_microseconds() << endl; } } @@ -444,9 +570,7 @@ Playlist::notify_length_changed () pending_length = false; LengthChanged(); /* EMIT SIGNAL */ pending_contents_change = false; - cerr << _name << "send4 contents change @ " << get_microseconds() << endl; ContentsChanged (); /* EMIT SIGNAL */ - cerr << _name << "done contents change @ " << get_microseconds() << endl; } } @@ -468,7 +592,7 @@ Playlist::flush_notifications () if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) { regions_changed = true; if (!pending_length) { - old_length = _get_maximum_extent (); + old_length = _get_extent ().second; check_length = true; } } @@ -491,26 +615,29 @@ Playlist::flush_notifications () for (s = pending_removes.begin(); s != pending_removes.end(); ++s) { remove_dependents (*s); - cerr << _name << " sends RegionRemoved\n"; + // cerr << _name << " sends RegionRemoved\n"; RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ } for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { - cerr << _name << " sends RegionAdded\n"; - RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ + // cerr << _name << " sends RegionAdded\n"; + /* don't emit RegionAdded signal until relayering is done, + so that the region is fully setup by the time + anyone hear's that its been added + */ dependent_checks_needed.insert (*s); } if (check_length) { - if (old_length != _get_maximum_extent()) { + if (old_length != _get_extent().second) { pending_length = true; - cerr << _name << " length has changed\n"; + // cerr << _name << " length has changed\n"; } } - if (pending_length || (freeze_length != _get_maximum_extent())) { + if (pending_length || (freeze_length != _get_extent().second)) { pending_length = false; - cerr << _name << " sends LengthChanged\n"; + // cerr << _name << " sends LengthChanged\n"; LengthChanged(); /* EMIT SIGNAL */ } @@ -519,17 +646,22 @@ Playlist::flush_notifications () relayer (); } pending_contents_change = false; - cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl; + // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl; ContentsChanged (); /* EMIT SIGNAL */ - cerr << _name << "done contents change @ " << get_microseconds() << endl; + // cerr << _name << "done contents change @ " << get_microseconds() << endl; } + for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { + (*s)->clear_history (); + RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ + } + for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { check_dependents (*s, false); } if (!pending_range_moves.empty ()) { - cerr << _name << " sends RangesMoved\n"; + // cerr << _name << " sends RangesMoved\n"; RangesMoved (pending_range_moves); } @@ -589,12 +721,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()); @@ -631,7 +763,7 @@ Playlist::add_region_internal (boost::shared_ptr region, framepos_t posi framecnt_t old_length = 0; if (!holding_state()) { - old_length = _get_maximum_extent(); + old_length = _get_extent().second; } if (!first_set_state) { @@ -648,8 +780,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 (); } @@ -661,7 +793,7 @@ Playlist::add_region_internal (boost::shared_ptr region, framepos_t posi check_dependents (region, false); - if (old_length != _get_maximum_extent()) { + if (old_length != _get_extent().second) { notify_length_changed (); } } @@ -702,7 +834,7 @@ Playlist::remove_region_internal (boost::shared_ptr region) int ret = -1; if (!holding_state()) { - old_length = _get_maximum_extent(); + old_length = _get_extent().second; } if (!in_set_state) { @@ -723,10 +855,10 @@ 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()) { + if (old_length != _get_extent().second) { notify_length_changed (); } } @@ -779,7 +911,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 (); } } @@ -804,7 +936,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) { @@ -859,11 +991,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()); @@ -878,11 +1010,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()); @@ -896,9 +1028,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) { @@ -916,11 +1048,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()); @@ -935,9 +1067,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) { @@ -959,11 +1091,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()); @@ -978,7 +1110,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) { @@ -1081,7 +1213,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; @@ -1097,7 +1229,7 @@ Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden) new_name += '.'; new_name += buf; - cnt = min (_get_maximum_extent() - start, cnt); + cnt = min (_get_extent().second - start, cnt); return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden); } @@ -1105,17 +1237,16 @@ int Playlist::paste (boost::shared_ptr other, framepos_t position, float times) { times = fabs (times); - framecnt_t old_length; { RegionLock rl1 (this); RegionLock rl2 (other.get()); - old_length = _get_maximum_extent(); + framecnt_t const old_length = _get_extent().second; int itimes = (int) floor (times); framepos_t pos = position; - framecnt_t shift = other->_get_maximum_extent(); + framecnt_t const shift = other->_get_extent().second; layer_t top_layer = regions.size(); while (itimes--) { @@ -1135,7 +1266,7 @@ Playlist::paste (boost::shared_ptr other, framepos_t position, float t /* XXX shall we handle fractional cases at some point? */ - if (old_length != _get_maximum_extent()) { + if (old_length != _get_extent().second) { notify_length_changed (); } @@ -1164,12 +1295,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); @@ -1183,7 +1314,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) { @@ -1204,7 +1335,7 @@ Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, b has to be done separately. */ - if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) { + if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) { fixup.push_back (*r); continue; } @@ -1221,7 +1352,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 */ @@ -1265,12 +1396,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); @@ -1278,12 +1409,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); @@ -1400,9 +1531,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; } @@ -1524,15 +1656,23 @@ 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; - cerr << _name << "send2 contents change @ " << get_microseconds() << endl; ContentsChanged (); - cerr << _name << "done with contents changed @ " << get_microseconds() << endl; } } @@ -1606,11 +1746,12 @@ Playlist::regions_to_read (framepos_t start, framepos_t end) RegionList covering; set to_check; set > unique; - RegionList here; to_check.insert (start); to_check.insert (end); + DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n"); + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { /* find all/any regions that span start+end */ @@ -1621,22 +1762,38 @@ Playlist::regions_to_read (framepos_t start, framepos_t end) case OverlapInternal: covering.push_back (*i); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name())); break; case OverlapStart: to_check.insert ((*i)->position()); + if ((*i)->position() != 0) { + to_check.insert ((*i)->position()-1); + } + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name())); covering.push_back (*i); break; case OverlapEnd: to_check.insert ((*i)->last_frame()); + to_check.insert ((*i)->last_frame()+1); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); covering.push_back (*i); break; case OverlapExternal: covering.push_back (*i); to_check.insert ((*i)->position()); + if ((*i)->position() != 0) { + to_check.insert ((*i)->position()-1); + } to_check.insert ((*i)->last_frame()); + to_check.insert ((*i)->last_frame()+1); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name())); break; } @@ -1654,18 +1811,30 @@ Playlist::regions_to_read (framepos_t start, framepos_t end) if (covering.size() == 1) { rlist->push_back (covering.front()); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name())); } else { + RegionList here; for (set::iterator t = to_check.begin(); t != to_check.end(); ++t) { here.clear (); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t)); + for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) { if ((*x)->covers (*t)) { here.push_back (*x); - } + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n", + (*x)->name(), + (*t))); + } else { + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n", + (*x)->name(), + (*t))); + } + } RegionSortByLayer cmp; @@ -1680,7 +1849,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end) if ((*c)->opaque()) { /* the other regions at this position are hidden by this one */ - + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n", + (*c)->name())); break; } } @@ -1698,6 +1868,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end) } } + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size())); + return rlist; } @@ -1935,6 +2107,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) { @@ -1964,6 +2215,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")) { @@ -1971,7 +2225,7 @@ Playlist::set_state (const XMLNode& node, int version) } } - clear (false); + clear (true); nlist = node.children(); @@ -1990,25 +2244,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 (); } } @@ -2020,7 +2274,6 @@ Playlist::set_state (const XMLNode& node, int version) check_dependents (*r, false); } - clear_pending (); // this makes thaw() do nothing thaw (); notify_contents_changed (); @@ -2049,6 +2302,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()); @@ -2084,27 +2338,29 @@ Playlist::n_regions() const return regions.size(); } -framecnt_t -Playlist::get_maximum_extent () const +pair +Playlist::get_extent () const { RegionLock rlock (const_cast(this), false); - return _get_maximum_extent (); + return _get_extent (); } -framecnt_t -Playlist::_get_maximum_extent () const +pair +Playlist::_get_extent () const { - RegionList::const_iterator i; - framecnt_t max_extent = 0; - framepos_t end = 0; + pair ext (max_frames, 0); - for (i = regions.begin(); i != regions.end(); ++i) { - if ((end = (*i)->position() + (*i)->length()) > max_extent) { - max_extent = end; + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + pair const e ((*i)->position(), (*i)->position() + (*i)->length()); + if (e.first < ext.first) { + ext.first = e.first; + } + if (e.second > ext.second) { + ext.second = e.second; } } - return max_extent; + return ext; } string @@ -2113,7 +2369,7 @@ Playlist::bump_name (string name, Session &session) string newname = name; do { - newname = bump_name_once (newname); + newname = bump_name_once (newname, '.'); } while (session.playlists->by_name (newname)!=NULL); return newname; @@ -2145,6 +2401,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 @@ -2156,7 +2418,7 @@ Playlist::relayer () int const divisions = 512; /* find the start and end positions of the regions on this playlist */ - framepos_t start = UINT_MAX; + framepos_t start = INT64_MAX; framepos_t end = 0; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { start = min (start, (*i)->position()); @@ -2173,7 +2435,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 */ @@ -2187,19 +2449,28 @@ 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 */ (*i)->set_pending_explicit_relayer (false); - /* find the time divisions that this region covers */ - int const start_division = floor ( ((*i)->position() - start) / division_size); - int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size ); - if (end_division == divisions) { - end_division--; + /* find the time divisions that this region covers; if there are no regions on the list, + division_size will equal 0 and in this case we'll just say that + start_division = end_division = 0. + */ + int start_division = 0; + int end_division = 0; + + if (division_size > 0) { + start_division = floor ( ((*i)->position() - start) / division_size); + end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size ); + if (end_division == divisions) { + end_division--; + } } - assert (end_division < divisions); + assert (divisions == 0 || end_division < divisions); /* find the lowest layer that this region can go on */ size_t j = layers.size(); @@ -2287,22 +2558,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 @@ -2311,7 +2608,6 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr RegionList::iterator i; typedef pair,layer_t> LayerInfo; list layerinfo; - layer_t dest; { RegionLock rlock (const_cast (this)); @@ -2322,6 +2618,8 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr continue; } + layer_t dest; + if (dir > 0) { /* region is moving up, move all regions on intermediate layers @@ -2382,7 +2680,6 @@ void Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) { RegionList::iterator i; - framepos_t new_pos; bool moved = false; _nudging = true; @@ -2394,6 +2691,8 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) if ((*i)->position() >= start) { + framepos_t new_pos; + if (forwards) { if ((*i)->last_frame() > max_frames - distance) { @@ -2441,7 +2740,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 */ @@ -2483,8 +2782,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); } @@ -2493,7 +2790,6 @@ void Playlist::shuffle (boost::shared_ptr region, int dir) { bool moved = false; - framepos_t new_pos; if (region->locked()) { return; @@ -2520,6 +2816,8 @@ Playlist::shuffle (boost::shared_ptr region, int dir) break; } + framepos_t new_pos; + if ((*next)->position() != region->last_frame() + 1) { /* they didn't used to touch, so after shuffle, just have them swap positions. @@ -2561,6 +2859,7 @@ Playlist::shuffle (boost::shared_ptr region, int dir) break; } + framepos_t new_pos; if (region->position() != (*prev)->last_frame() + 1) { /* they didn't used to touch, so after shuffle, just have them swap positions. @@ -2619,7 +2918,7 @@ void Playlist::update_after_tempo_map_change () { RegionLock rlock (const_cast (this)); - RegionList copy (regions); + RegionList copy (regions.rlist()); freeze (); @@ -2674,3 +2973,22 @@ Playlist::has_region_at (framepos_t const p) const return (i != regions.end()); } + +/** Remove any region that uses a given source */ +void +Playlist::remove_region_by_source (boost::shared_ptr s) +{ + RegionLock rl (this); + + RegionList::iterator i = regions.begin(); + while (i != regions.end()) { + RegionList::iterator j = i; + ++j; + + if ((*i)->uses_source (s)) { + remove_region_internal (*i); + } + + i = j; + } +}