basic uncombining (no post-facto region trimming)
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 22 May 2011 16:11:00 +0000 (16:11 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Sun, 22 May 2011 16:11:00 +0000 (16:11 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@9566 d708f5d6-7413-0410-9779-e7cbd77b26cf

15 files changed:
gtk2_ardour/ardour.menus.in
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_selection.cc
gtk2_ardour/route_time_axis.cc
gtk2_ardour/route_time_axis.h
libs/ardour/ardour/playlist.h
libs/ardour/ardour/playlist_source.h
libs/ardour/ardour/region.h
libs/ardour/ardour/region_factory.h
libs/ardour/ardour/session.h
libs/ardour/playlist.cc
libs/ardour/region.cc
libs/ardour/region_factory.cc
libs/ardour/session_state.cc

index 5fad03a1c15fde3006df5c9066ff045623af725a..af423cbb2303f71a5b1374a187d201ed85386248 100644 (file)
       <menuitem action='export-region'/>
       <menuitem action='bounce-region'/>
       <menuitem action='combine-regions'/>
+      <menuitem action='uncombine-regions'/>
       <menuitem action='analyze-region'/>
       <menuitem action='toggle-region-lock'/>
       <menuitem action='toggle-region-lock-style'/>
     <menuitem action='transpose-region'/>
     <menuitem action='naturalize-region'/>
     <menuitem action='combine-regions'/>
+    <menuitem action='uncombine-regions'/>
     <menuitem action='split-region'/>
     <menuitem action='split-multichannel-region'/>
     <menuitem action='remove-region'/>
index a45279b6bb605d6775862a8fa45b0b460093f2a2..74cc69e6dbd94c9d86e8e5676e023f9ae10ca7bc 100644 (file)
@@ -1373,6 +1373,7 @@ Editor::register_region_actions ()
 
        reg_sens (_region_actions, "bounce-region", _("Bounce"), sigc::mem_fun (*this, &Editor::bounce_region_selection));
        reg_sens (_region_actions, "combine-regions", _("Combine"), sigc::mem_fun (*this, &Editor::combine_regions));
+       reg_sens (_region_actions, "uncombine-regions", _("Uncombine"), sigc::mem_fun (*this, &Editor::uncombine_regions));
 
        reg_sens (_region_actions, "analyze-region", _("Spectral Analysis..."), sigc::mem_fun (*this, &Editor::analyze_region_selection));
 
index a6bba7d76c4a70990bc4e7680dada114923d2761..02bf220b4d268c12260ef2c7bb49603b7af3708a 100644 (file)
@@ -6434,3 +6434,30 @@ Editor::combine_regions ()
        commit_reversible_command ();
 }
 
+void
+Editor::uncombine_regions ()
+{
+       typedef set<RouteTimeAxisView*> RTVS;
+       RTVS tracks;
+
+       if (selection->regions.empty()) {
+               return;
+       }
+
+       for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
+
+               if (rtv) {
+                       tracks.insert (rtv);
+               }
+       }
+
+       begin_reversible_command (_("uncombine regions"));
+
+       for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
+               (*i)->uncombine_regions ();
+       }
+
+       commit_reversible_command ();
+}
+
index 3ec11ea41097d65edd1d5d6cd96e68bdf28da85f..38ee5dff3c6e5e185865dd06266812ba5bea862f 100644 (file)
@@ -989,6 +989,7 @@ Editor::sensitize_the_right_region_actions ()
        bool have_envelope_active = false;
        bool have_envelope_inactive = false;
        bool have_non_unity_scale_amplitude = false;
+       bool have_compound_regions = false;
 
        for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
 
@@ -1003,6 +1004,10 @@ Editor::sensitize_the_right_region_actions ()
                        have_midi = true;
                }
 
+               if (r->is_compound()) {
+                       have_compound_regions = true;
+               }
+
                if (r->locked()) {
                        have_locked = true;
                } else {
@@ -1084,6 +1089,10 @@ Editor::sensitize_the_right_region_actions ()
                _region_actions->get_action("place-transient")->set_sensitive (false);
        }
 
+       if (have_compound_regions) {
+               _region_actions->get_action("uncombine-regions")->set_sensitive (true);
+       }
+
        if (have_audio) {
                
                if (have_envelope_visible && !have_envelope_invisible) {
index 2dbc8635f73a6fbadc9b6d095a4deacc4bec9a13..16e1d72d53ead52c6debfe3217d32c6357124e41 100644 (file)
@@ -2503,6 +2503,35 @@ RouteTimeAxisView::combine_regions ()
        string name = string_compose (_("%1 compound-%2 (%3)"), playlist->name(), playlist->combine_ops()+1, max_level+1);
 
        playlist->clear_changes ();
-       playlist->join (selected_regions, name);
+       playlist->combine (selected_regions, name);
        _session->add_command (new StatefulDiffCommand (playlist));
 }
+
+void
+RouteTimeAxisView::uncombine_regions ()
+{
+       assert (is_track());
+
+       if (!_view) {
+               return;
+       }
+
+       Playlist::RegionList selected_regions;
+       boost::shared_ptr<Playlist> playlist = track()->playlist();
+       uint32_t max_level = 0;
+
+       /* have to grab selected regions first because the uncombine is going
+        * to change that in the middle of the list traverse
+        */
+
+       _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions, &max_level));
+
+       playlist->clear_changes ();
+
+       for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
+               playlist->uncombine (*i);
+       }
+
+       _session->add_command (new StatefulDiffCommand (playlist));
+}
+
index b06b56fc67618559feb0e40b3e4fd4cb9f909574..27e816738f2bea8f8d75d9254acbc94471ba8995 100644 (file)
@@ -95,6 +95,8 @@ public:
        void cut_copy_clear (Selection&, Editing::CutCopyOp);
        bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth);
        void combine_regions ();
+       void uncombine_regions ();
+       void uncombine_region (RegionView*);
        void toggle_automation_track (const Evoral::Parameter& param);
 
        /* The editor calls these when mapping an operation across multiple tracks */
index 6385def78594d57e35af60885bd738578649b90d..5d3d5d25049069068fc3d2fd0566a3dc64d11be6 100644 (file)
@@ -139,7 +139,8 @@ public:
        void partition (framepos_t start, framepos_t end, bool cut = false);
        void duplicate (boost::shared_ptr<Region>, framepos_t position, float times);
        void nudge_after (framepos_t start, framecnt_t distance, bool forwards);
-       void join (const RegionList&, const std::string&);
+       void combine (const RegionList&, const std::string&);
+       void uncombine (boost::shared_ptr<Region>);
 
        void shuffle (boost::shared_ptr<Region>, int dir);
        void update_after_tempo_map_change ();
@@ -382,6 +383,13 @@ public:
 
        typedef std::pair<boost::shared_ptr<Region>, boost::shared_ptr<Region> > TwoRegions;
        virtual void copy_dependents (const std::vector<TwoRegions>&, boost::shared_ptr<Playlist>) { }
+
+       struct RegionInfo {
+           boost::shared_ptr<Region> region;
+           framepos_t position;
+           framecnt_t length;
+           framepos_t start;
+       };
 };
 
 } /* namespace ARDOUR */
index 8732870ec071a9dfb75f02795b569c41227906a6..ed48fbabd5a1af945e8de3dffe665f7874b5790e 100644 (file)
@@ -36,7 +36,8 @@ class PlaylistSource : virtual public Source {
     virtual ~PlaylistSource ();
     
     int set_state (const XMLNode&, int version);
-    
+    boost::shared_ptr<const Playlist> playlist() const { return _playlist; }
+
   protected:
     boost::shared_ptr<Playlist>      _playlist;
     frameoffset_t                    _playlist_offset;
index 719c00acf8248bc1c2d4ce4c3718e043feebefba..fdcaeaf2491a974e2f812ed85aca92ea4e4883a1 100644 (file)
@@ -228,6 +228,8 @@ class Region
 
        void source_deleted (boost::weak_ptr<Source>);
 
+       bool is_compound () const;
+
        boost::shared_ptr<Source> source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; }
        uint32_t n_channels() const { return _sources.size(); }
 
index 647e1fe7d8b9f3b509a0a8d46500c2e29d211b5a..979b499b3ab6dae16bc9c0997463be4f3f19532b 100644 (file)
@@ -61,6 +61,7 @@ public:
        /** create a region from a single Source */
        static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, 
                                                 const PBD::PropertyList&, bool announce = true);
+       
        /** create a region from a multiple sources */
        static boost::shared_ptr<Region> create (const SourceList &, 
                                                 const PBD::PropertyList&, bool announce = true);
@@ -91,6 +92,24 @@ public:
        static int region_name (std::string &, std::string, bool new_level = false);
        static std::string new_region_name (std::string);
   
+       /* when we make a compound region, for every region involved there
+        * are two "instances" - the original, which is removed from this
+        * playlist, and a copy, which is added to the playlist used as 
+        * the source for the compound.
+        *
+        * when we uncombine, we want to put the originals back into this
+        * playlist after we remove the compound. this map lets us
+        * look them up easily. note that if the compound was trimmed or
+        * split, we may have to trim the originals
+        * and they may not be added back if the compound was trimmed
+        * or split sufficiently.
+        */
+
+       typedef std::map<boost::shared_ptr<Region>, boost::shared_ptr<Region> > CompoundAssociations;
+       static CompoundAssociations& compound_associations() { return _compound_associations; }
+
+       static void add_compound_association (boost::shared_ptr<Region>, boost::shared_ptr<Region>);
+       
   private:
 
        static void region_changed (PBD::PropertyChange const &, boost::weak_ptr<Region>);
@@ -106,6 +125,7 @@ public:
        static void update_region_name_map (boost::shared_ptr<Region>);
 
        static PBD::ScopedConnectionList region_list_connections;
+       static CompoundAssociations _compound_associations;
 };
 
 }
index dd282e6cf3c6bf5eb9bf5f409e6f1f6272019be3..f1e73ed1fd3affa4c7f3a98d25d3622e6fecd386 100644 (file)
@@ -1253,6 +1253,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
        mutable Glib::Mutex region_lock;
 
        int load_regions (const XMLNode& node);
+       int load_compounds (const XMLNode& node);
 
        void route_group_changed ();
 
index bc6e23e4513961bb640156a36131a3fdf8ff7a50..85935d4377b59927cb767ed1739e519633da4e1c 100644 (file)
@@ -39,6 +39,7 @@
 #include "ardour/region.h"
 #include "ardour/region_factory.h"
 #include "ardour/playlist_factory.h"
+#include "ardour/playlist_source.h"
 #include "ardour/transient_detector.h"
 #include "ardour/session_playlists.h"
 #include "ardour/source_factory.h"
@@ -2335,7 +2336,8 @@ Playlist::set_state (const XMLNode& node, int version)
                        // So that layer_op ordering doesn't get screwed up
                        region->set_last_layer_op( region->layer());
                        region->resume_property_changes ();
-               }
+
+               } 
        }
 
        /* update dependents, which was not done during add_region_internal
@@ -3151,7 +3153,7 @@ Playlist::find_next_top_layer_position (framepos_t t) const
 }
 
 void
-Playlist::join (const RegionList& r, const std::string& name)
+Playlist::combine (const RegionList& r, const std::string& name)
 {
        PropertyList plist; 
        uint32_t channels = 0;
@@ -3180,6 +3182,8 @@ Playlist::join (const RegionList& r, const std::string& name)
 
                old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
 
+               RegionFactory::add_compound_association (original_region, copied_region);
+
                /* make position relative to zero */
 
                pl->add_region (copied_region, original_region->position() - earliest_position);
@@ -3235,6 +3239,52 @@ Playlist::join (const RegionList& r, const std::string& name)
        thaw ();
 }
 
+void
+Playlist::uncombine (boost::shared_ptr<Region> target)
+{
+       // (1) check that its really a compound region
+       
+       boost::shared_ptr<PlaylistSource> pls;
+       boost::shared_ptr<const Playlist> pl;
+       vector<boost::shared_ptr<Region> > originals;
+
+       if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
+               return;
+       }
+
+       pl = pls->playlist();
+
+       // (2) get all the original regions
+
+       const RegionList& rl (pl->region_list().rlist());
+
+       RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+
+       for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
+               RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
+               if (ca != cassocs.end()) {
+                       originals.push_back (ca->second);
+               }
+       }
+
+       in_partition = true;
+       freeze ();
+
+       // (3) remove the compound region
+
+       remove_region (target);
+
+       // (4) add the originals. This will reset their playlist reference back
+       // to us, which means they are no longer considered owned by the RegionFactory
+
+       for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
+               add_region ((*i), (*i)->position());
+       }
+
+       in_partition = false;
+       thaw ();
+}
+
 uint32_t
 Playlist::max_source_level () const
 {
@@ -3263,4 +3313,3 @@ Playlist::count_joined_regions () const
 
        return cnt;
 }
-
index 93223709e78b4935a6f9f8e47028589220939b1f..3fbe288b7a6edfa3eb31c3700eb4b60556bc0219 100644 (file)
@@ -1671,3 +1671,9 @@ Region::max_source_level () const
 
        return lvl;
 }
+
+bool
+Region::is_compound () const
+{
+       return max_source_level() > 0;
+}
index eba237d0d208942bbe5aa70c17d7c0ca37f23f69..513ec7301bbd8547513c78643aad8b9427704660 100644 (file)
@@ -43,6 +43,7 @@ RegionFactory::RegionMap                      RegionFactory::region_map;
 PBD::ScopedConnectionList                     RegionFactory::region_list_connections;
 Glib::StaticMutex                             RegionFactory::region_name_map_lock;
 std::map<std::string, uint32_t>               RegionFactory::region_name_map;
+RegionFactory::CompoundAssociations           RegionFactory::_compound_associations;
 
 boost::shared_ptr<Region>
 RegionFactory::create (boost::shared_ptr<const Region> region, bool announce)
@@ -366,8 +367,8 @@ RegionFactory::clear_map ()
        {
                Glib::Mutex::Lock lm (region_map_lock);
                region_map.clear ();
+               _compound_associations.clear ();
        }
-
 }
 
 void
@@ -561,3 +562,10 @@ RegionFactory::remove_regions_using_source (boost::shared_ptr<Source> src)
                 }
        }
 }
+
+void
+RegionFactory::add_compound_association (boost::shared_ptr<Region> orig, boost::shared_ptr<Region> copy)
+{
+       Glib::Mutex::Lock lm (region_map_lock);
+       _compound_associations[copy] = orig;
+}
index 82c2d5a31c313681fad7cb9518bb8451cd82582d..02aee5d3ef74aad52c0a17470d6773050b8fc12c 100644 (file)
@@ -1088,6 +1088,22 @@ Session::state(bool full_state)
                                 child->add_child_nocopy (r->state ());
                         }
                 }
+               
+               RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+
+               if (!cassocs.empty()) {
+                       XMLNode* ca = node->add_child (X_("CompoundAssociations"));
+
+                       for (RegionFactory::CompoundAssociations::iterator i = cassocs.begin(); i != cassocs.end(); ++i) {
+                               char buf[64];
+                               XMLNode* can = new XMLNode (X_("CompoundAssociation"));
+                               i->first->id().print (buf, sizeof (buf));
+                               can->add_property (X_("copy"), buf);
+                               i->second->id().print (buf, sizeof (buf));
+                               can->add_property (X_("original"), buf);
+                               ca->add_child_nocopy (*can);
+                       }
+               }  
        }
 
        if (full_state) {
@@ -1318,6 +1334,12 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
        
+       if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
+               if (load_compounds (*child)) {
+                       goto out;
+               }
+       }
+       
        if ((child = find_named_node (node, "NamedSelections")) != 0) {
                if (load_named_selections (*child)) {
                        goto out;
@@ -1592,6 +1614,44 @@ Session::load_regions (const XMLNode& node)
        return 0;
 }
 
+int
+Session::load_compounds (const XMLNode& node)
+{
+       XMLNodeList calist = node.children();
+       XMLNodeConstIterator caiter;
+       XMLProperty *caprop;
+       
+       for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
+               XMLNode* ca = *caiter;
+               ID orig_id;
+               ID copy_id;
+               
+               if ((caprop = ca->property (X_("original"))) == 0) {
+                       continue;
+               }
+               orig_id = caprop->value();
+               
+               if ((caprop = ca->property (X_("copy"))) == 0) {
+                       continue;
+               }
+               copy_id = caprop->value();
+               
+               boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
+               boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
+               
+               if (!orig || !copy) {
+                       warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
+                                                  orig_id, copy_id) 
+                               << endmsg;
+                       continue;
+               }
+               
+               RegionFactory::add_compound_association (orig, copy);
+       }
+
+       return 0;
+}
+
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {