mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
basic uncombining (no post-facto region trimming)
git-svn-id: svn://localhost/ardour2/branches/3.0@9566 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
d40ee95486
commit
9c733915a0
15 changed files with 232 additions and 7 deletions
|
|
@ -229,6 +229,7 @@
|
|||
<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'/>
|
||||
|
|
@ -590,6 +591,7 @@
|
|||
<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'/>
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1671,3 +1671,9 @@ Region::max_source_level () const
|
|||
|
||||
return lvl;
|
||||
}
|
||||
|
||||
bool
|
||||
Region::is_compound () const
|
||||
{
|
||||
return max_source_level() > 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue