diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 859df381b0..aa1416138b 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -76,6 +76,7 @@ namespace Properties { LIBARDOUR_API extern PBD::PropertyDescriptor shift; LIBARDOUR_API extern PBD::PropertyDescriptor layering_index; LIBARDOUR_API extern PBD::PropertyDescriptor tags; + LIBARDOUR_API extern PBD::PropertyDescriptor reg_group; LIBARDOUR_API extern PBD::PropertyDescriptor contents; // type doesn't matter here, used for signal only }; @@ -155,6 +156,52 @@ public: timepos_t ancestral_start () const { return _ancestral_start.val(); } timecnt_t ancestral_length () const { return _ancestral_length.val(); } + /** Region Groups: + * every region has a group-id. regions that have the same group-id (excepting zero) are 'grouped' + * if you select a 'grouped' region, then all other regions in the group will be selected + * operations like Import, Record, and Paste assign a group-id to the new regions they create + * users can explicitly group regions, which implies a stronger connection and gets the 'explicit' flag + * users can explicitly ungroup regions, which prevents ardour from applying equivalent-regions logic + * regions with no flags and no group-id (prior sessions) will revert to equivalent-regions logic */ + + /** RegionGroupRetainer is an RAII construct to retain a group-id for the length of an operation that creates regions */ + struct RegionGroupRetainer { + RegionGroupRetainer () + { + if (_retained_group_id == 0) { + _next_group_id++; + _retained_group_id = _next_group_id << 4; + _clear_on_destruction = true; + } else { + _clear_on_destruction = false; + } + } + ~RegionGroupRetainer () + { + if (_clear_on_destruction) { + _retained_group_id = 0; + } + } + bool _clear_on_destruction; + }; + + static uint64_t next_group_id () { return _next_group_id; } + static void set_next_group_id (uint64_t ngid) { _next_group_id = ngid; } + + /* access the shared group-id, potentially with an Alt identifier (for left/center/right splits) */ + static uint64_t get_retained_group_id (bool alt = false) + { + return _retained_group_id | (alt ? Alt : NoGroup); + } + + uint64_t region_group () const { return _reg_group; } + void set_region_group (bool explicitly, bool alt = false) { _reg_group = get_retained_group_id (alt) | (explicitly ? Explicit : NoGroup); } + void unset_region_group () { _reg_group = Explicit; } + + bool is_explicitly_grouped() { return (_reg_group & Explicit) == Explicit; } + bool is_implicitly_ungrouped() { return (_reg_group == NoGroup); } + bool is_explicitly_ungrouped() { return (_reg_group == Explicit); } + float stretch () const { return _stretch; } float shift () const { return _shift; } @@ -536,6 +583,7 @@ private: PBD::Property _shift; PBD::Property _layering_index; PBD::Property _tags; + PBD::Property _reg_group; PBD::Property _contents; // type is irrelevant timecnt_t _last_length; @@ -548,6 +596,14 @@ private: void use_sources (SourceList const &); + enum RegionGroupFlags : uint64_t { + NoGroup = 0x0, //no flag: implicitly grouped if the id is nonzero; or implicitly 'un-grouped' if the group-id is zero + Explicit = 0x1, //the user has explicitly grouped or ungrouped this region. explicitly grouped regions can cross track-group boundaries + Alt = 0x8, //this accommodates some operations (separate) that generate left/center/right region groups. add more bits here, if needed + }; + static uint64_t _retained_group_id; + static uint64_t _next_group_id; + std::atomic _source_deleted; Glib::Threads::Mutex _source_list_lock; PBD::ScopedConnectionList _source_deleted_connections; diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 148ed8aa85..3dd0b01685 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -80,6 +80,7 @@ namespace ARDOUR { PBD::PropertyDescriptor shift; PBD::PropertyDescriptor layering_index; PBD::PropertyDescriptor tags; + PBD::PropertyDescriptor reg_group; PBD::PropertyDescriptor contents; /* these properties are used as a convenience for announcing changes to state, but aren't stored as properties */ @@ -90,6 +91,10 @@ namespace ARDOUR { PBD::Signal2,const PropertyChange&> Region::RegionsPropertyChanged; +/* these static values are used by Region Groups to assign a group-id across the scope of an operation that might span many function calls */ +uint64_t Region::_retained_group_id = 0; +uint64_t Region::_next_group_id = 0; + void Region::make_property_quarks () { @@ -147,6 +152,8 @@ Region::make_property_quarks () DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for contents = %1\n", Properties::contents.property_id)); Properties::time_domain.property_id = g_quark_from_static_string (X_("time_domain")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for time_domain = %1\n", Properties::time_domain.property_id)); + Properties::reg_group.property_id = g_quark_from_static_string (X_("rgroup")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for region_group = %1\n", Properties::reg_group.property_id)); } void @@ -177,6 +184,7 @@ Region::register_properties () add_property (_shift); add_property (_layering_index); add_property (_tags); + add_property (_reg_group); add_property (_contents); } @@ -208,6 +216,7 @@ Region::register_properties () , _shift (Properties::shift, 1.0) \ , _layering_index (Properties::layering_index, 0) \ , _tags (Properties::tags, "") \ + , _reg_group (Properties::reg_group, 0) \ , _contents (Properties::contents, false) #define REGION_COPY_STATE(other) \ @@ -240,6 +249,7 @@ Region::register_properties () , _shift (Properties::shift, other->_shift) \ , _layering_index (Properties::layering_index, other->_layering_index) \ , _tags (Properties::tags, other->_tags) \ + , _reg_group (Properties::reg_group, other->_reg_group) \ , _contents (Properties::contents, other->_contents) /* derived-from-derived constructor (no sources in constructor) */