From 9c733915a0bc4b5274fac749b1adc874da79a6ee Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sun, 22 May 2011 16:11:00 +0000 Subject: basic uncombining (no post-facto region trimming) git-svn-id: svn://localhost/ardour2/branches/3.0@9566 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour.menus.in | 2 ++ gtk2_ardour/editor_actions.cc | 1 + gtk2_ardour/editor_ops.cc | 27 ++++++++++++++++ gtk2_ardour/editor_selection.cc | 9 ++++++ gtk2_ardour/route_time_axis.cc | 31 ++++++++++++++++++- gtk2_ardour/route_time_axis.h | 2 ++ libs/ardour/ardour/playlist.h | 10 +++++- libs/ardour/ardour/playlist_source.h | 3 +- libs/ardour/ardour/region.h | 2 ++ libs/ardour/ardour/region_factory.h | 20 ++++++++++++ libs/ardour/ardour/session.h | 1 + libs/ardour/playlist.cc | 55 +++++++++++++++++++++++++++++++-- libs/ardour/region.cc | 6 ++++ libs/ardour/region_factory.cc | 10 +++++- libs/ardour/session_state.cc | 60 ++++++++++++++++++++++++++++++++++++ 15 files changed, 232 insertions(+), 7 deletions(-) diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 5fad03a1c1..af423cbb23 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -229,6 +229,7 @@ + @@ -590,6 +591,7 @@ + diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index a45279b6bb..74cc69e6db 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -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)); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index a6bba7d76c..02bf220b4d 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -6434,3 +6434,30 @@ Editor::combine_regions () commit_reversible_command (); } +void +Editor::uncombine_regions () +{ + typedef set RTVS; + RTVS tracks; + + if (selection->regions.empty()) { + return; + } + + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + RouteTimeAxisView* rtv = dynamic_cast(&(*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 (); +} + diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 3ec11ea410..38ee5dff3c 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -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::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) { diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 2dbc8635f7..16e1d72d53 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -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 = 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)); +} + diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index b06b56fc67..27e816738f 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -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 */ diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 6385def785..5d3d5d2504 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -139,7 +139,8 @@ public: void partition (framepos_t start, framepos_t end, bool cut = false); void duplicate (boost::shared_ptr, 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); void shuffle (boost::shared_ptr, int dir); void update_after_tempo_map_change (); @@ -382,6 +383,13 @@ public: typedef std::pair, boost::shared_ptr > TwoRegions; virtual void copy_dependents (const std::vector&, boost::shared_ptr) { } + + struct RegionInfo { + boost::shared_ptr region; + framepos_t position; + framecnt_t length; + framepos_t start; + }; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/playlist_source.h b/libs/ardour/ardour/playlist_source.h index 8732870ec0..ed48fbabd5 100644 --- a/libs/ardour/ardour/playlist_source.h +++ b/libs/ardour/ardour/playlist_source.h @@ -36,7 +36,8 @@ class PlaylistSource : virtual public Source { virtual ~PlaylistSource (); int set_state (const XMLNode&, int version); - + boost::shared_ptr playlist() const { return _playlist; } + protected: boost::shared_ptr _playlist; frameoffset_t _playlist_offset; diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 719c00acf8..fdcaeaf249 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -228,6 +228,8 @@ class Region void source_deleted (boost::weak_ptr); + bool is_compound () const; + boost::shared_ptr source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; } uint32_t n_channels() const { return _sources.size(); } diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index 647e1fe7d8..979b499b3a 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -61,6 +61,7 @@ public: /** create a region from a single Source */ static boost::shared_ptr create (boost::shared_ptr, const PBD::PropertyList&, bool announce = true); + /** create a region from a multiple sources */ static boost::shared_ptr 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 > CompoundAssociations; + static CompoundAssociations& compound_associations() { return _compound_associations; } + + static void add_compound_association (boost::shared_ptr, boost::shared_ptr); + private: static void region_changed (PBD::PropertyChange const &, boost::weak_ptr); @@ -106,6 +125,7 @@ public: static void update_region_name_map (boost::shared_ptr); static PBD::ScopedConnectionList region_list_connections; + static CompoundAssociations _compound_associations; }; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index dd282e6cf3..f1e73ed1fd 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -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 (); diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index bc6e23e451..85935d4377 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -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 target) +{ + // (1) check that its really a compound region + + boost::shared_ptr pls; + boost::shared_ptr pl; + vector > originals; + + if ((pls = boost::dynamic_pointer_cast(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 >::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; } - diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 93223709e7..3fbe288b7a 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -1671,3 +1671,9 @@ Region::max_source_level () const return lvl; } + +bool +Region::is_compound () const +{ + return max_source_level() > 0; +} diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index eba237d0d2..513ec7301b 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -43,6 +43,7 @@ RegionFactory::RegionMap RegionFactory::region_map; PBD::ScopedConnectionList RegionFactory::region_list_connections; Glib::StaticMutex RegionFactory::region_name_map_lock; std::map RegionFactory::region_name_map; +RegionFactory::CompoundAssociations RegionFactory::_compound_associations; boost::shared_ptr RegionFactory::create (boost::shared_ptr 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 src) } } } + +void +RegionFactory::add_compound_association (boost::shared_ptr orig, boost::shared_ptr copy) +{ + Glib::Mutex::Lock lm (region_map_lock); + _compound_associations[copy] = orig; +} diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 82c2d5a31c..02aee5d3ef 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -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 orig = RegionFactory::region_by_id (orig_id); + boost::shared_ptr 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 Session::XMLRegionFactory (const XMLNode& node, bool full) { -- cgit v1.2.3