Fix small issue with the playlist unique name patch.
[ardour.git] / libs / ardour / playlist.cc
index f53283a98e3f74870490f87daf4d4f3c90882600..9ed103b67ce8e8ac8d81e3efc4e4e2f204d470eb 100644 (file)
@@ -37,6 +37,7 @@
 #include <ardour/region.h>
 #include <ardour/region_factory.h>
 #include <ardour/playlist_factory.h>
+#include <ardour/transient_detector.h>
 
 #include "i18n.h"
 
@@ -228,6 +229,7 @@ Playlist::init (bool hide)
        _refcnt = 0;
        _hidden = hide;
        _splicing = false;
+       _shuffling = false;
        _nudging = false;
        in_set_state = 0;
        _edit_mode = Config->get_edit_mode();
@@ -280,7 +282,17 @@ Playlist::set_name (string str)
                return;
        }
 
-       _name = str; 
+       if (str == _name) {
+               return;
+       }
+
+       string name = str;
+
+       while (_session.playlist_by_name(name) != 0) {
+               name = bump_name_once(name);
+       }
+
+       _name = name; 
        NameChanged(); /* EMIT SIGNAL */
 }
 
@@ -487,7 +499,7 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa
        }
 
 
-       possibly_splice_unlocked (position, (pos + length) - position);
+       possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
 }
 
 void
@@ -524,6 +536,8 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
        regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
        all_regions.insert (region);
 
+       possibly_splice_unlocked (position, region->length(), region);
+
        if (!holding_state () && !in_set_state) {
                /* layers get assigned from XML state */
                relayer ();
@@ -564,7 +578,6 @@ void
 Playlist::remove_region (boost::shared_ptr<Region> region)
 {
        RegionLock rlock (this);
-       nframes_t pos = region->position();
        remove_region_internal (region);
 }
 
@@ -1048,7 +1061,7 @@ Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_pos
 }
 
 void
-Playlist::possibly_splice (nframes_t at, nframes64_t distance)
+Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
 {
        if (_splicing || in_set_state) {
                /* don't respond to splicing moves or state setting */
@@ -1056,12 +1069,12 @@ Playlist::possibly_splice (nframes_t at, nframes64_t distance)
        }
 
        if (_edit_mode == Splice) {
-               splice_locked (at, distance);
+               splice_locked (at, distance, exclude);
        }
 }
 
 void
-Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance)
+Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
 {
        if (_splicing || in_set_state) {
                /* don't respond to splicing moves or state setting */
@@ -1069,38 +1082,36 @@ Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance)
        }
 
        if (_edit_mode == Splice) {
-               splice_unlocked (at, distance);
+               splice_unlocked (at, distance, exclude);
        }
 }
 
 void
-Playlist::splice_locked (nframes_t at, nframes64_t distance)
+Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
 {
        {
                RegionLock rl (this);
-               core_splice (at, distance);
+               core_splice (at, distance, exclude);
        }
-
-       notify_length_changed ();
 }
 
 void
-Playlist::splice_unlocked (nframes_t at, nframes64_t distance)
+Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
 {
-       core_splice (at, distance);
-       notify_length_changed ();
+       core_splice (at, distance, exclude);
 }
 
 void
-Playlist::core_splice (nframes_t at, nframes64_t distance)
+Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
 {
-       stacktrace (cerr, 12);
-
        _splicing = true;
 
-       cerr << "core splice, move everything >= " << at << " by " << distance << endl;
-       
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+               if (exclude && (*i) == exclude) {
+                       continue;
+               }
+
                if ((*i)->position() >= at) {
                        nframes64_t new_pos = (*i)->position() + distance;
                        if (new_pos < 0) {
@@ -1109,19 +1120,19 @@ Playlist::core_splice (nframes_t at, nframes64_t distance)
                                new_pos = max_frames - (*i)->length();
                        } 
                                
-                       cerr << "\tmove " << (*i)->name() << " to " << new_pos << endl;
                        (*i)->set_position (new_pos, this);
                }
        }
 
-       
        _splicing = false;
+
+       notify_length_changed ();
 }
 
 void
 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
 {
-       if (in_set_state || _splicing || _nudging) {
+       if (in_set_state || _splicing || _nudging || _shuffling) {
                return;
        }
 
@@ -1144,10 +1155,24 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region>
 
                regions.erase (i);
                regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
-
        }
 
        if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
+               
+               nframes64_t delta = 0;
+               
+               if (what_changed & ARDOUR::PositionChanged) {
+                       delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
+               } 
+               
+               if (what_changed & ARDOUR::LengthChanged) {
+                       delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
+               } 
+
+               if (delta) {
+                       possibly_splice (region->last_position() + region->last_length(), delta, region);
+               }
+
                if (holding_state ()) {
                        pending_bounds.push_back (region);
                } else {
@@ -1155,9 +1180,7 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region>
                                /* it moved or changed length, so change the timestamp */
                                timestamp_layer_op (region);
                        }
-
-                       // XXX NEED TO SPLICE HERE ... HOW TO GET DISTANCE ?
-
+                       
                        notify_length_changed ();
                        relayer ();
                        check_dependents (region, false);
@@ -1399,6 +1422,65 @@ Playlist::regions_touched (nframes_t start, nframes_t end)
        return rlist;
 }
 
+nframes64_t
+Playlist::find_next_transient (nframes64_t from, int dir)
+{
+       RegionLock rlock (this);
+       AnalysisFeatureList points;
+       AnalysisFeatureList these_points;
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if (dir > 0) {
+                       if ((*i)->last_frame() < from) {
+                               continue;
+                       }
+               } else {
+                       if ((*i)->first_frame() > from) {
+                               continue;
+                       }
+               }
+
+               (*i)->get_transients (these_points);
+
+               /* add first frame, just, err, because */
+               
+               these_points.push_back ((*i)->first_frame());
+               
+               points.insert (points.end(), these_points.begin(), these_points.end());
+               these_points.clear ();
+       }
+       
+       if (points.empty()) {
+               return -1;
+       }
+
+       TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+       bool reached = false;
+       
+       if (dir > 0) {
+               for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+                       if ((*x) >= from) {
+                               reached = true;
+                       }
+                       
+                       if (reached && (*x) > from) {
+                               return *x;
+                       }
+               }
+       } else {
+               for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+                       if ((*x) <= from) {
+                               reached = true;
+                       }
+                       
+                       if (reached && (*x) < from) {
+                               return *x;
+                       }
+               }
+       }
+
+       return -1;
+}
 
 boost::shared_ptr<Region>
 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
@@ -1453,6 +1535,92 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
        return ret;
 }
 
+nframes64_t
+Playlist::find_next_region_boundary (nframes64_t frame, int dir)
+{
+       RegionLock rlock (this);
+
+       nframes64_t closest = max_frames;
+       nframes64_t ret = -1;
+
+       if (dir > 0) {
+
+               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                       
+                       boost::shared_ptr<Region> r = (*i);
+                       nframes64_t distance;
+                       nframes64_t end = r->position() + r->length();
+                       bool reset;
+
+                       reset = false;
+
+                       if (r->first_frame() > frame) {
+
+                               distance = r->first_frame() - frame;
+                               
+                               if (distance < closest) {
+                                       ret = r->first_frame();
+                                       closest = distance;
+                                       reset = true;
+                               }
+                       }
+
+                       if (end > frame) {
+                               
+                               distance = end - frame;
+                               
+                               if (distance < closest) {
+                                       ret = end;
+                                       closest = distance;
+                                       reset = true;
+                               }
+                       }
+
+                       if (reset) {
+                               break;
+                       }
+               }
+
+       } else {
+
+               for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
+                       
+                       boost::shared_ptr<Region> r = (*i);
+                       nframes64_t distance;
+                       bool reset;
+
+                       reset = false;
+
+                       if (r->last_frame() < frame) {
+
+                               distance = frame - r->last_frame();
+                               
+                               if (distance < closest) {
+                                       ret = r->last_frame();
+                                       closest = distance;
+                                       reset = true;
+                               }
+                       }
+
+                       if (r->first_frame() < frame) {
+                               distance = frame - r->last_frame();
+                               
+                               if (distance < closest) {
+                                       ret = r->first_frame();
+                                       closest = distance;
+                                       reset = true;
+                               }
+                       }
+
+                       if (reset) {
+                               break;
+                       }
+               }
+       }
+
+       return ret;
+}
+
 /***********************************************************************/
 
 
@@ -1638,50 +1806,12 @@ Playlist::bump_name (string name, Session &session)
        string newname = name;
 
        do {
-               newname = Playlist::bump_name_once (newname);
+               newname = bump_name_once (newname);
        } while (session.playlist_by_name (newname)!=NULL);
 
        return newname;
 }
 
-string
-Playlist::bump_name_once (string name)
-{
-       string::size_type period;
-       string newname;
-
-       if ((period = name.find_last_of ('.')) == string::npos) {
-               newname  = name;
-               newname += ".1";
-       } else {
-               int isnumber = 1;
-               const char *last_element = name.c_str() + period + 1;
-               for (size_t i = 0; i < strlen(last_element); i++) {
-                       if (!isdigit(last_element[i])) {
-                               isnumber = 0;
-                               break;
-                       }
-               }
-
-               errno = 0;
-               long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
-
-               if (isnumber == 0 || errno != 0) {
-                       // last_element is not a number, or is too large
-                       newname  = name;
-                       newname += ".1";
-               } else {
-                       char buf[32];
-
-                       snprintf (buf, sizeof(buf), "%ld", version+1);
-               
-                       newname  = name.substr (0, period+1);
-                       newname += buf;
-               }
-       }
-
-       return newname;
-}
 
 layer_t
 Playlist::top_layer() const
@@ -1985,3 +2115,144 @@ Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
        region->set_last_layer_op (++layer_op_counter);
 }
 
+
+void
+Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
+{
+       bool moved = false;
+       nframes_t new_pos;
+
+       if (region->locked()) {
+               return;
+       }
+
+       _shuffling = true;
+
+       {
+               RegionLock rlock (const_cast<Playlist*> (this));
+               
+               
+               if (dir > 0) {
+                       
+                       RegionList::iterator next;
+
+                       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {       
+                               if ((*i) == region) {
+                                       next = i;
+                                       ++next;
+
+                                       if (next != regions.end()) {
+
+                                               if ((*next)->locked()) {
+                                                       break;
+                                               }
+
+                                               if ((*next)->position() != region->last_frame() + 1) {
+                                                       /* they didn't used to touch, so after shuffle,
+                                                          just have them swap positions.
+                                                       */
+                                                       new_pos = (*next)->position();
+                                               } else {
+                                                       /* they used to touch, so after shuffle,
+                                                          make sure they still do. put the earlier
+                                                          region where the later one will end after
+                                                          it is moved.
+                                                       */
+                                                       new_pos = region->position() + (*next)->length();
+                                               }
+
+                                               (*next)->set_position (region->position(), this);
+                                               region->set_position (new_pos, this);
+
+                                               /* avoid a full sort */
+
+                                               regions.erase (i); // removes the region from the list */
+                                               next++;
+                                               regions.insert (next, region); // adds it back after next
+
+                                               moved = true;
+                                       }
+                                       break;
+                               }
+                       }
+               } else {
+                       
+                       RegionList::iterator prev = regions.end();
+                       
+                       for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {     
+                               if ((*i) == region) {
+
+                                       if (prev != regions.end()) {
+
+                                               if ((*prev)->locked()) {
+                                                       break;
+                                               }
+
+                                               if (region->position() != (*prev)->last_frame() + 1) {
+                                                       /* they didn't used to touch, so after shuffle,
+                                                          just have them swap positions.
+                                                       */
+                                                       new_pos = region->position();
+                                               } else {
+                                                       /* they used to touch, so after shuffle,
+                                                          make sure they still do. put the earlier
+                                                          one where the later one will end after
+                                                       */
+                                                       new_pos = (*prev)->position() + region->length();
+                                               }
+
+                                               region->set_position ((*prev)->position(), this);
+                                               (*prev)->set_position (new_pos, this);
+                                               
+                                               /* avoid a full sort */
+
+                                               regions.erase (i); // remove region
+                                               regions.insert (prev, region); // insert region before prev
+
+                                               moved = true;
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       _shuffling = false;
+
+       if (moved) {
+
+               relayer ();
+               check_dependents (region, false);
+               
+               notify_modified();
+       }
+
+}
+
+bool
+Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>) 
+{
+       RegionLock rlock (const_cast<Playlist*> (this));
+       
+       if (regions.size() > 1) {
+               return true;
+       }
+
+       return false;
+}
+
+void
+Playlist::update_after_tempo_map_change ()
+{
+       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionList copy (regions);
+
+       freeze ();
+       
+       for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {     
+               (*i)->update_position_after_tempo_map_change ();
+       }
+
+       thaw ();
+}