Fix region-gain offset when separating ranges
[ardour.git] / libs / ardour / playlist.cc
index b109ef2f287faabb4750aa3323bdded9f23218b0..39a6dc77ed01dbb2ad98e5a3267b1f468d2b0315 100644 (file)
 #include <algorithm>
 #include <string>
 
-#include <boost/lexical_cast.hpp>
-
-#include "pbd/convert.h"
+#include "pbd/types_convert.h"
 #include "pbd/stateful_diff_command.h"
 #include "pbd/strsplit.h"
 #include "pbd/xml++.h"
 
 #include "ardour/debug.h"
+#include "ardour/midi_region.h"
 #include "ardour/playlist.h"
-#include "ardour/session.h"
+#include "ardour/playlist_factory.h"
+#include "ardour/playlist_source.h"
 #include "ardour/region.h"
 #include "ardour/region_factory.h"
 #include "ardour/region_sorters.h"
-#include "ardour/playlist_factory.h"
-#include "ardour/playlist_source.h"
-#include "ardour/transient_detector.h"
+#include "ardour/session.h"
 #include "ardour/session_playlists.h"
 #include "ardour/source_factory.h"
+#include "ardour/tempo.h"
+#include "ardour/transient_detector.h"
+#include "ardour/types_convert.h"
 
 #include "pbd/i18n.h"
 
@@ -105,16 +106,16 @@ RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNod
           code, so we can just store ID here.
        */
 
-       node.add_property ("id", region->id().to_s ());
+       node.set_property ("id", region->id());
 }
 
 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 ());
+       PBD::ID id;
+       if (!node.get_property ("id", id)) {
+               assert (false);
+       }
 
        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
 
@@ -292,7 +293,7 @@ Playlist::copy_regions (RegionList& newlist) const
        RegionReadLock rlock (const_cast<Playlist *> (this));
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               newlist.push_back (RegionFactory::create (*i, true));
+               newlist.push_back (RegionFactory::create (*i, true, true));
        }
 }
 
@@ -361,11 +362,7 @@ Playlist::_set_sort_id ()
        } else {
                string t = _name.val().substr(dot_position + 1);
 
-               try {
-                       _sort_id = boost::lexical_cast<int>(t);
-               }
-
-               catch (boost::bad_lexical_cast e) {
+               if (!string_to_uint32 (t, _sort_id)) {
                        _sort_id = 0;
                }
        }
@@ -669,7 +666,7 @@ Playlist::clear_pending ()
 
 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
 void
-Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, const int32_t sub_num)
+Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, int32_t sub_num, double quarter_note, bool for_music)
 {
        RegionWriteLock rlock (this);
        times = fabs (times);
@@ -688,19 +685,18 @@ Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, flo
        }
 
        if (itimes >= 1) {
-               add_region_internal (region, pos, sub_num);
+               add_region_internal (region, pos, sub_num, quarter_note, for_music);
                set_layer (region, DBL_MAX);
                pos += region->length();
                --itimes;
        }
 
-
        /* note that itimes can be zero if we being asked to just
           insert a single fraction of the region.
        */
 
        for (int i = 0; i < itimes; ++i) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
+               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                add_region_internal (copy, pos, sub_num);
                set_layer (copy, DBL_MAX);
                pos += region->length();
@@ -743,7 +739,7 @@ Playlist::set_region_ownership ()
 }
 
 bool
-Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
+Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, int32_t sub_num, double quarter_note, bool for_music)
 {
        if (region->data_type() != _type) {
                return false;
@@ -755,8 +751,11 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t posi
                boost::shared_ptr<Playlist> foo (shared_from_this());
                region->set_playlist (boost::weak_ptr<Playlist>(foo));
        }
-
-       region->set_position (position, sub_num);
+       if (for_music) {
+               region->set_position_music (quarter_note);
+       } else {
+               region->set_position (position, sub_num);
+       }
 
        regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
        all_regions.insert (region);
@@ -891,6 +890,20 @@ Playlist::partition (framepos_t start, framepos_t end, bool cut)
        }
 }
 
+/* If a MIDI region is locked to musical-time, Properties::start is ignored
+ * and _start is overwritten using Properties::start_beats in
+ * add_region_internal() -> Region::set_position() -> MidiRegion::set_position_internal()
+ */
+static void maybe_add_start_beats (TempoMap const& tm, PropertyList& plist, boost::shared_ptr<Region> r, framepos_t start, framepos_t end)
+{
+       boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
+       if (!mr) {
+               return;
+       }
+       double delta_beats = tm.quarter_notes_between_frames (start, end);
+       plist.add (Properties::start_beats, mr->start_beats () + delta_beats);
+}
+
 /** Go through each region on the playlist and cut them at start and end, removing the section between
  *  start and end if cutting == true.  Regions that lie entirely within start and end are always
  *  removed.
@@ -983,8 +996,12 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
                                        plist.add (Properties::automatic, true);
                                        plist.add (Properties::left_of_split, true);
                                        plist.add (Properties::right_of_split, true);
+                                       maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
 
-                                       region = RegionFactory::create (current, plist);
+                                       /* see note in :_split_region()
+                                        * for MusicFrame is needed to offset region-gain
+                                        */
+                                       region = RegionFactory::create (current, MusicFrame (pos2 - pos1, 0), plist);
                                        add_region_internal (region, start);
                                        new_regions.push_back (region);
                                }
@@ -1002,8 +1019,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
                                plist.add (Properties::layering_index, current->layering_index ());
                                plist.add (Properties::automatic, true);
                                plist.add (Properties::right_of_split, true);
+                               maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos3 - pos1));
 
-                               region = RegionFactory::create (current, plist);
+                               region = RegionFactory::create (current, MusicFrame (pos3 - pos1, 0), plist);
 
                                add_region_internal (region, end);
                                new_regions.push_back (region);
@@ -1042,8 +1060,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
                                        plist.add (Properties::layering_index, current->layering_index ());
                                        plist.add (Properties::automatic, true);
                                        plist.add (Properties::left_of_split, true);
+                                       maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
 
-                                       region = RegionFactory::create (current, plist);
+                                       region = RegionFactory::create (current, MusicFrame(pos2 - pos1, 0), plist);
 
                                        add_region_internal (region, start);
                                        new_regions.push_back (region);
@@ -1087,6 +1106,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
                                        plist.add (Properties::layering_index, current->layering_index ());
                                        plist.add (Properties::automatic, true);
                                        plist.add (Properties::right_of_split, true);
+                                       maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start());
 
                                        region = RegionFactory::create (current, plist);
 
@@ -1411,7 +1431,7 @@ Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
  }
 
  void
- Playlist::split (framepos_t at, const int32_t sub_num)
+ Playlist::split (const MusicFrame& at)
  {
         RegionWriteLock rlock (this);
         RegionList copy (regions.rlist());
@@ -1420,33 +1440,34 @@ Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
          */
 
         for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
-                _split_region (*r, at, sub_num);
+                _split_region (*r, at);
         }
  }
 
  void
- Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
+ Playlist::split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
  {
         RegionWriteLock rl (this);
-        _split_region (region, playlist_position, sub_num);
+        _split_region (region, playlist_position);
  }
 
  void
- Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
+ Playlist::_split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
  {
-        if (!region->covers (playlist_position)) {
+        if (!region->covers (playlist_position.frame)) {
                 return;
         }
 
-        if (region->position() == playlist_position ||
-            region->last_frame() == playlist_position) {
+        if (region->position() == playlist_position.frame ||
+            region->last_frame() == playlist_position.frame) {
                 return;
         }
 
         boost::shared_ptr<Region> left;
         boost::shared_ptr<Region> right;
-        frameoffset_t before;
-        frameoffset_t after;
+
+        MusicFrame before (playlist_position.frame - region->position(), playlist_position.division);
+        MusicFrame after (region->length() - before.frame, 0);
         string before_name;
         string after_name;
 
@@ -1455,15 +1476,12 @@ Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
         bool old_sp = _splicing;
         _splicing = true;
 
-        before = playlist_position - region->position();
-        after = region->length() - before;
-
         RegionFactory::region_name (before_name, region->name(), false);
 
         {
                 PropertyList plist;
 
-                plist.add (Properties::length, before);
+                plist.add (Properties::length, before.frame);
                 plist.add (Properties::name, before_name);
                 plist.add (Properties::left_of_split, true);
                 plist.add (Properties::layering_index, region->layering_index ());
@@ -1473,7 +1491,7 @@ Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
                    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, true, sub_num);
+                left = RegionFactory::create (region, MusicFrame (0, 0), plist, true);
         }
 
         RegionFactory::region_name (after_name, region->name(), false);
@@ -1481,18 +1499,19 @@ Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
         {
                 PropertyList plist;
 
-                plist.add (Properties::length, after);
+                plist.add (Properties::length, after.frame);
                 plist.add (Properties::name, after_name);
                 plist.add (Properties::right_of_split, true);
                 plist.add (Properties::layering_index, region->layering_index ());
                 plist.add (Properties::layer, region->layer ());
 
                 /* same note as above */
-                right = RegionFactory::create (region, before, plist, true, sub_num);
+                right = RegionFactory::create (region, before, plist, true);
         }
 
-        add_region_internal (left, region->position());
-        add_region_internal (right, region->position() + before);
+        add_region_internal (left, region->position(), 0);
+        add_region_internal (right, region->position() + before.frame, before.division);
+
         remove_region_internal (region);
 
         _splicing = old_sp;
@@ -2200,9 +2219,7 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
         XMLNode *child;
         XMLNodeList nlist;
         XMLNodeConstIterator niter;
-        XMLPropertyList plist;
         XMLPropertyConstIterator piter;
-        XMLProperty const * prop;
         boost::shared_ptr<Region> region;
         string region_name;
         bool seen_region_nodes = false;
@@ -2217,36 +2234,31 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
 
         freeze ();
 
-        plist = node.properties();
-
         set_id (node);
 
-        for (piter = plist.begin(); piter != plist.end(); ++piter) {
-
-                prop = *piter;
-
-                if (prop->name() == X_("name")) {
-                        _name = prop->value();
-                        _set_sort_id ();
-                } else if (prop->name() == X_("orig-diskstream-id")) {
-                        /* XXX legacy session: fix up later */
-                        _orig_track_id = prop->value ();
-                } else if (prop->name() == X_("orig-track-id")) {
-                        _orig_track_id = prop->value ();
-                } else if (prop->name() == X_("frozen")) {
-                        _frozen = string_is_affirmative (prop->value());
-                } else if (prop->name() == X_("combine-ops")) {
-                        _combine_ops = atoi (prop->value());
-                } else if (prop->name() == X_("shared-with-ids")) {
-                        string shared_ids = prop->value ();
-                        if (!shared_ids.empty()) {
-                               vector<string> result;
-                               ::split (shared_ids, result, ',');
-                               vector<string>::iterator it = result.begin();
-                               for (; it != result.end(); ++it) {
-                                       _shared_with_ids.push_back (PBD::ID(*it));
-                               }
-                        }
+        std::string name;
+        if (node.get_property (X_("name"), name)) {
+                _name = name;
+                _set_sort_id ();
+        }
+
+        /* XXX legacy session: fix up later */
+        node.get_property (X_("orig-diskstream-id"), _orig_track_id);
+
+        node.get_property (X_("orig-track-id"), _orig_track_id);
+        node.get_property (X_("frozen"), _frozen);
+
+        node.get_property (X_("combine-ops"), _combine_ops);
+
+        string shared_ids;
+        if (node.get_property (X_("shared-with-ids"), shared_ids)) {
+                if (!shared_ids.empty()) {
+                       vector<string> result;
+                       ::split (shared_ids, result, ',');
+                       vector<string>::iterator it = result.begin();
+                       for (; it != result.end(); ++it) {
+                               _shared_with_ids.push_back (PBD::ID(*it));
+                       }
                 }
         }
 
@@ -2262,13 +2274,12 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
 
                         seen_region_nodes = true;
 
-                        if ((prop = child->property ("id")) == 0) {
+                        ID id;
+                        if (!child->get_property ("id", id)) {
                                 error << _("region state node has no ID, ignored") << endmsg;
                                 continue;
                         }
 
-                        ID id = prop->value ();
-
                         if ((region = region_by_id (id))) {
 
                                 region->suspend_property_changes ();
@@ -2326,14 +2337,11 @@ XMLNode&
 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());
 
-       _orig_track_id.print (buf, sizeof (buf));
-       node->add_property (X_("orig-track-id"), buf);
+       node->set_property (X_("id"), id());
+       node->set_property (X_("name"), name());
+       node->set_property (X_("type"), _type);
+       node->set_property (X_("orig-track-id"), _orig_track_id);
 
        string shared_ids;
        list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
@@ -2344,14 +2352,13 @@ Playlist::state (bool full_state)
                shared_ids.erase(0,1);
        }
 
-       node->add_property (X_("shared-with-ids"), shared_ids);
-       node->add_property (X_("frozen"), _frozen ? "yes" : "no");
+       node->set_property (X_("shared-with-ids"), shared_ids);
+       node->set_property (X_("frozen"), _frozen);
 
        if (full_state) {
                RegionReadLock rlock (this);
 
-               snprintf (buf, sizeof (buf), "%u", _combine_ops);
-               node->add_property ("combine-ops", buf);
+               node->set_property ("combine-ops", _combine_ops);
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                        node->add_child_nocopy ((*i)->get_state());
@@ -3318,11 +3325,18 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
 void
 Playlist::fade_range (list<AudioRange>& ranges)
 {
-        for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
-                for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-                        (*i)->fade_range ((*r).start, (*r).end);
-                }
-        }
+       RegionReadLock rlock (this);
+       for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
+               list<AudioRange>::iterator tmpr = r;
+               ++tmpr;
+               for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
+                       RegionList::const_iterator tmpi = i;
+                       ++tmpi;
+                       (*i)->fade_range ((*r).start, (*r).end);
+                       i = tmpi;
+               }
+               r = tmpr;
+       }
 }
 
 uint32_t