Tweak transmission of MMC on locate. Should fix #3121.
[ardour.git] / libs / ardour / playlist.cc
index 5968f67f215353de7e719e58825dad61cd5f998c..cc9dc62ec79c027fcc3f8417c11ab7821bb5d190 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
 #include <set>
 #include <fstream>
 #include <algorithm>
@@ -131,16 +134,30 @@ RegionListProperty::copy_for_history () const
 }
 
 void 
-RegionListProperty::diff (PropertyList& before, PropertyList& after) const
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo, Command* cmd) const
 {
-        if (_have_old) {
+        if (changed()) {
+               /* list of the removed/added regions since clear_history() was last called */
                 RegionListProperty* a = copy_for_history ();
-                RegionListProperty* b = copy_for_history ();
 
+               /* the same list, but with removed/added lists swapped (for undo purposes) */
+                RegionListProperty* b = copy_for_history ();
                 b->invert_changes ();
 
-                before.add (b);
-                after.add (a);
+                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);
         }
 }
 
@@ -162,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 */
@@ -176,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);
 
@@ -211,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()));
 
@@ -264,7 +283,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
                        break;
                }
 
-               _session.region_name (new_name, region->name(), false);
+               RegionFactory::region_name (new_name, region->name(), false);
 
                PropertyList plist; 
 
@@ -450,7 +469,7 @@ void
 Playlist::delay_notifications ()
 {
        g_atomic_int_inc (&block_notifications);
-       freeze_length = _get_maximum_extent();
+       freeze_length = _get_extent().second;
 }
 
 void
@@ -533,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;
@@ -572,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;
                }
        }
@@ -601,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 */
@@ -628,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);
        }
@@ -693,7 +721,7 @@ Playlist::add_region (boost::shared_ptr<Region> 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;
@@ -735,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) {
@@ -765,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 ();
                }
        }
@@ -806,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) {
@@ -830,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 ();
                                }
                        }
@@ -963,7 +991,7 @@ 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;
                                        
@@ -982,7 +1010,7 @@ 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;
                                
@@ -1002,7 +1030,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapEnd) {
 
@@ -1020,7 +1048,7 @@ 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;
                                        
@@ -1041,7 +1069,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapStart) {
 
@@ -1063,7 +1091,7 @@ 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;
                                        
@@ -1201,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);
 }
 
@@ -1214,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--) {
@@ -1238,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 ();
                }
 
@@ -1267,7 +1295,7 @@ Playlist::duplicate (boost::shared_ptr<Region> 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;
@@ -1307,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;
                }
@@ -1368,7 +1396,7 @@ Playlist::_split_region (boost::shared_ptr<Region> 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;
@@ -1381,7 +1409,7 @@ Playlist::_split_region (boost::shared_ptr<Region> 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;
@@ -1722,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 */
@@ -1732,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;
                }
 
@@ -1765,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 {
 
@@ -1773,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;
@@ -1792,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;
                                }
                        }
@@ -1810,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;
 }
 
@@ -2278,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
@@ -2307,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;
@@ -2339,7 +2401,7 @@ Playlist::set_edit_mode (EditMode mode)
 void
 Playlist::relayer ()
 {
-        /* never compute layers when changing state for undo/redo or setting from XML*/
+        /* never compute layers when changing state for undo/redo or setting from XML */
 
         if (in_update || in_set_state) {
                 return;
@@ -2356,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());
@@ -2393,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();
@@ -2488,22 +2558,48 @@ void
 Playlist::raise_region_to_top (boost::shared_ptr<Region> 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> 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
@@ -2686,8 +2782,6 @@ Playlist::set_frozen (bool yn)
 void
 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
 {
-//     struct timeval tv;
-//     gettimeofday (&tv, 0);
        region->set_last_layer_op (++layer_op_counter);
 }
 
@@ -2879,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;
+       }
+}