Tweak transmission of MMC on locate. Should fix #3121.
[ardour.git] / libs / ardour / playlist.cc
index 6fc7fdc2cffa9d92020485a07aa77d672b850647..cc9dc62ec79c027fcc3f8417c11ab7821bb5d190 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
 #include <set>
 #include <fstream>
 #include <algorithm>
@@ -131,7 +134,7 @@ RegionListProperty::copy_for_history () const
 }
 
 void 
-RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo, Command* cmd) const
 {
         if (changed()) {
                /* list of the removed/added regions since clear_history() was last called */
@@ -141,6 +144,18 @@ RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
                 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<boost::shared_ptr<Region> >::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);
         }
@@ -164,8 +179,10 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide
         , _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 */
@@ -178,7 +195,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
        : 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);
 
@@ -213,7 +230,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
        : 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<Playlist*> (other.get()));
 
@@ -452,7 +469,7 @@ void
 Playlist::delay_notifications ()
 {
        g_atomic_int_inc (&block_notifications);
-       freeze_length = _get_maximum_extent();
+       freeze_length = _get_extent().second;
 }
 
 void
@@ -535,6 +552,7 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
                pending_contents_change = true;
                pending_length = true;
        } else {
+                r->clear_history ();
                pending_length = false;
                LengthChanged (); /* EMIT SIGNAL */
                pending_contents_change = false;
@@ -574,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;
                }
        }
@@ -603,18 +621,21 @@ Playlist::flush_notifications ()
 
        for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
                // cerr << _name << " sends RegionAdded\n";
-               RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+                /* 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";
                }
        }
 
-       if (pending_length || (freeze_length != _get_maximum_extent())) {
+       if (pending_length || (freeze_length != _get_extent().second)) {
                pending_length = false;
                // cerr << _name << " sends LengthChanged\n";
                LengthChanged(); /* EMIT SIGNAL */
@@ -630,6 +651,11 @@ Playlist::flush_notifications ()
                // 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<Region> (*s)); /* EMIT SIGNAL */
+        }
+
        for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
                check_dependents (*s, false);
        }
@@ -737,7 +763,7 @@ Playlist::add_region_internal (boost::shared_ptr<Region> 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) {
@@ -767,7 +793,7 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t posi
 
                check_dependents (region, false);
 
-               if (old_length != _get_maximum_extent()) {
+               if (old_length != _get_extent().second) {
                        notify_length_changed ();
                }
        }
@@ -808,7 +834,7 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
        int ret = -1;
 
        if (!holding_state()) {
-               old_length = _get_maximum_extent();
+               old_length = _get_extent().second;
        }
 
        if (!in_set_state) {
@@ -832,7 +858,7 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
                                 relayer ();
                                remove_dependents (region);
 
-                               if (old_length != _get_maximum_extent()) {
+                               if (old_length != _get_extent().second) {
                                        notify_length_changed ();
                                }
                        }
@@ -1203,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);
 }
 
@@ -1216,11 +1242,11 @@ Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float t
                RegionLock rl1 (this);
                RegionLock rl2 (other.get());
 
-               framecnt_t 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--) {
@@ -1240,7 +1266,7 @@ Playlist::paste (boost::shared_ptr<Playlist> 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 ();
                }
 
@@ -1309,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;
                }
@@ -1724,6 +1750,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
        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 */
@@ -1734,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;
                }
 
@@ -1767,6 +1811,7 @@ 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 {
 
@@ -1775,11 +1820,21 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
 
                        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;
@@ -1794,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;
                                }
                        }
@@ -1812,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;
 }
 
@@ -2280,27 +2338,29 @@ Playlist::n_regions() const
        return regions.size();
 }
 
-framecnt_t
-Playlist::get_maximum_extent () const
+pair<framecnt_t, framecnt_t>
+Playlist::get_extent () const
 {
        RegionLock rlock (const_cast<Playlist *>(this), false);
-       return _get_maximum_extent ();
+       return _get_extent ();
 }
 
-framecnt_t
-Playlist::_get_maximum_extent () const
+pair<framecnt_t, framecnt_t>
+Playlist::_get_extent () const
 {
-       RegionList::const_iterator i;
-       framecnt_t max_extent = 0;
-       framepos_t end = 0;
+       pair<framecnt_t, framecnt_t> 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<framecnt_t, framecnt_t> 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
@@ -2309,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;
@@ -2358,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());
@@ -2395,14 +2455,22 @@ Playlist::relayer ()
                /* 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();
@@ -2905,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<Source> 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;
+       }
+}