Fix VST build.
[ardour.git] / libs / ardour / playlist.cc
index 2044a2f1426c6b8d8d2fa1e0781b6c671e9efca7..971ed8b2e836173a56b0c8d91d24aa23c1e15e6d 100644 (file)
@@ -17,9 +17,7 @@
 
 */
 
-#define __STDC_LIMIT_MACROS
 #include <stdint.h>
-
 #include <set>
 #include <fstream>
 #include <algorithm>
@@ -33,6 +31,7 @@
 #include "pbd/failed_constructor.h"
 #include "pbd/stateful_diff_command.h"
 #include "pbd/xml++.h"
+#include "pbd/stacktrace.h"
 
 #include "ardour/debug.h"
 #include "ardour/playlist.h"
@@ -110,43 +109,53 @@ RegionListProperty::RegionListProperty (Playlist& pl)
         : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
         , _playlist (pl)
 {
+       
 }
 
-boost::shared_ptr<Region>
-RegionListProperty::lookup_id (const ID& id)
+RegionListProperty::RegionListProperty (RegionListProperty const & p)
+       : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
+       , _playlist (p._playlist)
 {
-        boost::shared_ptr<Region> ret =  _playlist.region_by_id (id);
-        
-        if (!ret) {
-                ret = RegionFactory::region_by_id (id);
-        }
 
-        return ret;
 }
 
-RegionListProperty*
-RegionListProperty::copy_for_history () const
+RegionListProperty *
+RegionListProperty::clone () const
 {
-        RegionListProperty* copy = new RegionListProperty (_playlist);
-        /* this is all we need */
-        copy->_change = _change;
-        return copy;
+       return new RegionListProperty (*this);
 }
 
-void 
-RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
+RegionListProperty *
+RegionListProperty::create () const
 {
-        if (changed()) {
-               /* list of the removed/added regions since clear_history() was last called */
-                RegionListProperty* a = copy_for_history ();
+       return new RegionListProperty (_playlist);
+}
 
-               /* the same list, but with removed/added lists swapped (for undo purposes) */
-                RegionListProperty* b = copy_for_history ();
-                b->invert_changes ();
+void
+RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
+{
+       /* All regions (even those which are deleted) have their state saved by other
+          code, so we can just store ID here.
+       */
+       
+       node.add_property ("id", region->id().to_s ());
+}
 
-                undo.add (b);
-                redo.add (a);
+boost::shared_ptr<Region>
+RegionListProperty::get_content_from_xml (XMLNode const & node) const
+{
+       XMLProperty const * prop = node.property ("id");
+       assert (prop);
+
+       PBD::ID id (prop->value ());
+
+        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
+        
+        if (!ret) {
+                ret = RegionFactory::region_by_id (id);
         }
+
+        return ret;
 }
 
 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
@@ -167,8 +176,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 */
@@ -314,7 +325,7 @@ Playlist::copy_regions (RegionList& newlist) const
        RegionLock rlock (const_cast<Playlist *> (this));
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               newlist.push_back (RegionFactory::RegionFactory::create (*i));
+               newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
        }
 }
 
@@ -432,7 +443,7 @@ Playlist::begin_undo ()
 void
 Playlist::end_undo ()
 {
-       thaw ();
+       thaw (true);
         in_update = false;
 }
 
@@ -443,11 +454,12 @@ Playlist::freeze ()
        g_atomic_int_inc (&ignore_state_changes);
 }
 
+/** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
 void
-Playlist::thaw ()
+Playlist::thaw (bool from_undo)
 {
        g_atomic_int_dec_and_test (&ignore_state_changes);
-       release_notifications ();
+       release_notifications (from_undo);
 }
 
 
@@ -458,11 +470,12 @@ Playlist::delay_notifications ()
        freeze_length = _get_extent().second;
 }
 
+/** @param from_undo true if this release is triggered by the end of an undo on this playlist */
 void
-Playlist::release_notifications ()
+Playlist::release_notifications (bool from_undo)
 {
        if (g_atomic_int_dec_and_test (&block_notifications)) {
-               flush_notifications ();
+               flush_notifications (from_undo);
         }
 
 }
@@ -521,7 +534,7 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
 
                list< Evoral::RangeMove<framepos_t> > m;
                m.push_back (move);
-               RangesMoved (m);
+               RangesMoved (m, false);
        }
 
 }
@@ -532,12 +545,13 @@ Playlist::notify_region_added (boost::shared_ptr<Region> 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_changes ();
                pending_length = false;
                LengthChanged (); /* EMIT SIGNAL */
                pending_contents_change = false;
@@ -559,8 +573,9 @@ Playlist::notify_length_changed ()
        }
 }
 
+/** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
 void
-Playlist::flush_notifications ()
+Playlist::flush_notifications (bool from_undo)
 {
        set<boost::shared_ptr<Region> > dependent_checks_needed;
        set<boost::shared_ptr<Region> >::iterator s;
@@ -606,7 +621,10 @@ 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);
        }
 
@@ -633,13 +651,17 @@ Playlist::flush_notifications ()
                // cerr << _name << "done contents change @ " << get_microseconds() << endl;
        }
 
+       for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
+                (*s)->clear_changes ();
+               RegionAdded (boost::weak_ptr<Region> (*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";
-               RangesMoved (pending_range_moves);
+               RangesMoved (pending_range_moves, from_undo);
        }
        
        clear_pending ();
@@ -673,7 +695,7 @@ Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, flo
        framepos_t pos = position;
 
        if (times == 1 && auto_partition){
-               partition(pos, (pos + region->length()), true);
+               partition(pos - 1, (pos + region->length()), true);
        }
 
        if (itimes >= 1) {
@@ -688,7 +710,7 @@ Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, flo
        */
 
        for (int i = 0; i < itimes; ++i) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region);
+               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                add_region_internal (copy, pos);
                pos += region->length();
        }
@@ -846,9 +868,7 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
                }
        }
 
-       /* XXX and thaw ... */
-
-       return ret;
+       return -1;
 }
 
 void
@@ -1228,14 +1248,14 @@ Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float t
 
                while (itimes--) {
                        for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
-                               boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
+                               boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
 
                                /* put these new regions on top of all existing ones, but preserve
                                   the ordering they had in the original playlist.
                                */
 
                                copy_of_region->set_layer (copy_of_region->layer() + top_layer);
-                               add_region_internal (copy_of_region, copy_of_region->position() + pos);
+                               add_region_internal (copy_of_region, (*i)->position() + pos);
                        }
                        pos += shift;
                }
@@ -1261,10 +1281,10 @@ Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, floa
 
        RegionLock rl (this);
        int itimes = (int) floor (times);
-       framepos_t pos = position;
+       framepos_t pos = position + 1;
 
        while (itimes--) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region);
+               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                add_region_internal (copy, pos);
                pos += region->length();
        }
@@ -1312,7 +1332,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;
                }
@@ -1378,12 +1398,15 @@ Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_p
        {
                PropertyList plist;
                
-               plist.add (Properties::start, region->start());
                plist.add (Properties::length, before);
                plist.add (Properties::name, before_name);
                plist.add (Properties::left_of_split, true);
-               
-               left = RegionFactory::create (region, plist);
+
+               /* note: we must use the version of ::create with an offset here,
+                  since it supplies that offset to the Region constructor, which
+                  is necessary to get audio region gain envelopes right.
+               */
+               left = RegionFactory::create (region, 0, plist);
        }
 
        RegionFactory::region_name (after_name, region->name(), false);
@@ -1391,12 +1414,12 @@ Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_p
        {
                PropertyList plist;
                
-               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);
 
-               right = RegionFactory::create (region, plist);
+               /* same note as above */
+               right = RegionFactory::create (region, before, plist);
        }
 
        add_region_internal (left, region->position());
@@ -1477,8 +1500,8 @@ Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Reg
                        framepos_t new_pos = (*i)->position() + distance;
                        if (new_pos < 0) {
                                new_pos = 0;
-                       } else if (new_pos >= max_frames - (*i)->length()) {
-                               new_pos = max_frames - (*i)->length();
+                       } else if (new_pos >= max_framepos - (*i)->length()) {
+                               new_pos = max_framepos - (*i)->length();
                        }
 
                        (*i)->set_position (new_pos, this);
@@ -1596,7 +1619,7 @@ Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<
                check_dependents (region, false);
        }
 
-       if (what_changed.contains (Properties::position)) {
+       if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
                notify_region_moved (region);
        }
 
@@ -1666,6 +1689,21 @@ Playlist::regions_at (framepos_t frame)
        return find_regions_at (frame);
 }
 
+uint32_t
+Playlist::count_regions_at (framepos_t frame)
+{
+       RegionLock rlock (this);
+       uint32_t cnt = 0;
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->covers (frame)) {
+                       cnt++;
+               }
+       }
+
+       return cnt;
+}
+
 boost::shared_ptr<Region>
 Playlist::top_region_at (framepos_t frame)
 
@@ -1946,7 +1984,7 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
 {
        RegionLock rlock (this);
        boost::shared_ptr<Region> ret;
-       framepos_t closest = max_frames;
+       framepos_t closest = max_framepos;
 
        bool end_iter = false;
 
@@ -1967,7 +2005,6 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
                        break;
                case SyncPoint:
                        pos = r->sync_position ();
-                       // r->adjust_to_sync (r->first_frame());
                        break;
                }
 
@@ -2008,7 +2045,7 @@ Playlist::find_next_region_boundary (framepos_t frame, int dir)
 {
        RegionLock rlock (this);
 
-       framepos_t closest = max_frames;
+       framepos_t closest = max_framepos;
        framepos_t ret = -1;
 
        if (dir > 0) {
@@ -2071,6 +2108,7 @@ Playlist::find_next_region_boundary (framepos_t frame, int dir)
        return ret;
 }
 
+
 /***********************************************************************/
 
 
@@ -2084,38 +2122,18 @@ Playlist::mark_session_dirty ()
        }
 }
 
-bool
-Playlist::set_property (const PropertyBase& prop)
-{
-        if (prop == Properties::regions.property_id) {
-                const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
-                regions.update (change);
-                return (!change.added.empty() && !change.removed.empty());
-        }
-        return false;
-}
-
 void
-Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
+Playlist::rdiff (vector<Command*>& cmds) const
 {
        RegionLock rlock (const_cast<Playlist *> (this));
-
-       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->changed ()) {
-                        StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
-                        cmds.push_back (sdc);
-                }
-       }
+       Stateful::rdiff (cmds);
 }
 
 void
-Playlist::clear_owned_history ()
+Playlist::clear_owned_changes ()
 {
        RegionLock rlock (this);
-
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                (*i)->clear_history ();
-        }
+       Stateful::clear_owned_changes ();
 }
 
 void
@@ -2137,32 +2155,6 @@ Playlist::update (const RegionListProperty::ChangeRecord& change)
         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<Playlist*> (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)
 {
@@ -2234,7 +2226,8 @@ Playlist::set_state (const XMLNode& node, int version)
                                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
@@ -2277,7 +2270,7 @@ XMLNode&
 Playlist::state (bool full_state)
 {
        XMLNode *node = new XMLNode (X_("Playlist"));
-       char buf[64];
+       char buf[64] = "";
 
        node->add_property (X_("id"), id().to_s());
        node->add_property (X_("name"), _name);
@@ -2289,6 +2282,7 @@ Playlist::state (bool full_state)
 
        if (full_state) {
                RegionLock rlock (this, false);
+
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                        node->add_child_nocopy ((*i)->get_state());
                }
@@ -2315,20 +2309,20 @@ Playlist::n_regions() const
        return regions.size();
 }
 
-pair<framecnt_t, framecnt_t>
+pair<framepos_t, framepos_t>
 Playlist::get_extent () const
 {
        RegionLock rlock (const_cast<Playlist *>(this), false);
        return _get_extent ();
 }
 
-pair<framecnt_t, framecnt_t>
+pair<framepos_t, framepos_t>
 Playlist::_get_extent () const
 {
-       pair<framecnt_t, framecnt_t> ext (max_frames, 0);
+       pair<framepos_t, framepos_t> ext (max_framepos, 0);
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               pair<framecnt_t, framecnt_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
+               pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
                if (e.first < ext.first) {
                        ext.first = e.first;
                }
@@ -2432,14 +2426,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();
@@ -2499,10 +2501,10 @@ Playlist::relayer ()
 void
 Playlist::raise_region (boost::shared_ptr<Region> region)
 {
-       uint32_t rsz = regions.size();
+       uint32_t top = regions.size() - 1;
        layer_t target = region->layer() + 1U;
 
-       if (target >= rsz) {
+       if (target >= top) {
                /* its already at the effective top */
                return;
        }
@@ -2624,6 +2626,8 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
                }
        }
 
+        freeze ();
+
        /* now reset the layers without holding the region lock */
 
        for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
@@ -2632,15 +2636,16 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
 
        region->set_layer (target_layer);
 
-#if 0
-       /* now check all dependents */
+        /* now check all dependents, since we changed the layering */
 
        for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
                check_dependents (x->first, false);
        }
 
        check_dependents (region, false);
-#endif
+        notify_layering_changed ();
+
+        thaw ();
 
        return 0;
 }
@@ -2664,8 +2669,8 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
 
                                if (forwards) {
 
-                                       if ((*i)->last_frame() > max_frames - distance) {
-                                               new_pos = max_frames - (*i)->length();
+                                       if ((*i)->last_frame() > max_framepos - distance) {
+                                               new_pos = max_framepos - (*i)->length();
                                        } else {
                                                new_pos = (*i)->position() + distance;
                                        }
@@ -2708,8 +2713,23 @@ Playlist::find_region (const ID& id) const
        return boost::shared_ptr<Region> ();
 }
 
+uint32_t
+Playlist::region_use_count (boost::shared_ptr<Region> r) const
+{
+       RegionLock rlock (const_cast<Playlist*> (this));
+        uint32_t cnt = 0;
+
+       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i) == r) {
+                        cnt++;
+               }
+       }
+
+       return cnt;
+}
+
 boost::shared_ptr<Region>
-Playlist::region_by_id (const ID& id)
+Playlist::region_by_id (const ID& id) const
 {
        /* searches all regions ever added to this playlist */
 
@@ -2961,3 +2981,27 @@ Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
                i = j;
        }
 }
+
+/** Look from a session frame time and find the start time of the next region
+ *  which is on the top layer of this playlist.
+ *  @param t Time to look from.
+ *  @return Position of next top-layered region, or max_framepos if there isn't one.
+ */
+framepos_t
+Playlist::find_next_top_layer_position (framepos_t t) const
+{
+       RegionLock rlock (const_cast<Playlist *> (this));
+       
+       layer_t const top = top_layer ();
+
+       RegionList copy = regions.rlist ();
+       copy.sort (RegionSortByPosition ());
+
+       for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+               if ((*i)->position() >= t && (*i)->layer() == top) {
+                       return (*i)->position();
+               }
+       }
+
+       return max_framepos;
+}