From 45328723b0228a83075a505c08a5c6a2f95ef9c1 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 29 Dec 2023 09:15:11 -0700 Subject: [PATCH] separate out all bounds/position info from Region into "Slice" The idea here is to be able to describe region size, start and position independently of an actual Region object. --- libs/ardour/ardour/region.h | 90 +---------------- libs/ardour/ardour/slice.h | 122 +++++++++++++++++++++++ libs/ardour/region.cc | 139 +++----------------------- libs/ardour/slice.cc | 193 ++++++++++++++++++++++++++++++++++++ libs/ardour/wscript | 1 + 5 files changed, 330 insertions(+), 215 deletions(-) create mode 100644 libs/ardour/ardour/slice.h create mode 100644 libs/ardour/slice.cc diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 9313d84a2d..72333a8dd1 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -39,6 +39,7 @@ #include "ardour/movable.h" #include "ardour/readable.h" #include "ardour/session_object.h" +#include "ardour/slice.h" #include "ardour/trimmable.h" #include "ardour/types_convert.h" @@ -65,8 +66,6 @@ namespace Properties { LIBARDOUR_API extern PBD::PropertyDescriptor hidden; LIBARDOUR_API extern PBD::PropertyDescriptor position_locked; LIBARDOUR_API extern PBD::PropertyDescriptor valid_transients; // used for signal only - LIBARDOUR_API extern PBD::PropertyDescriptor start; - LIBARDOUR_API extern PBD::PropertyDescriptor length; LIBARDOUR_API extern PBD::PropertyDescriptor sync_position; LIBARDOUR_API extern PBD::PropertyDescriptor layer; LIBARDOUR_API extern PBD::PropertyDescriptor ancestral_start; @@ -102,6 +101,7 @@ enum LIBARDOUR_API RegionOperationFlag { class LIBARDOUR_API Region : public SessionObject , public std::enable_shared_from_this + , public Slice , public Trimmable , public Movable , public Temporal::TimeDomainSwapper @@ -130,29 +130,6 @@ public: void start_domain_bounce (Temporal::DomainBounceInfo&); void finish_domain_bounce (Temporal::DomainBounceInfo&); - /** How the region parameters play together: - * - * POSITION: first sample of the region along the timeline - * START: first sample of the region within its source(s) - * LENGTH: number of samples the region represents - */ - - timepos_t position () const { return _length.val().position(); } - timepos_t start () const { return _start.val(); } - timecnt_t length () const { return _length.val(); } - timepos_t end() const; - timepos_t nt_last() const { return end().decrement(); } - - virtual timecnt_t tail () const { return timecnt_t (0); } - - timepos_t source_position () const; - timecnt_t source_relative_position (Temporal::timepos_t const &) const; - timecnt_t region_relative_position (Temporal::timepos_t const &) const; - - samplepos_t position_sample () const { return position().samples(); } - samplecnt_t start_sample () const { return _start.val().samples(); } - samplecnt_t length_samples () const { return _length.val().samples(); } - layer_t layer () const { return _layer; } void set_selected_for_solo(bool yn); @@ -160,11 +137,6 @@ public: timepos_t source_length (uint32_t n) const; uint32_t max_source_level () const; - /* these two are valid ONLY during a StateChanged signal handler */ - - timepos_t last_position () const { return _last_length.position(); } - timecnt_t last_length () const { return _last_length; } - samplecnt_t ancestral_start_sample () const { return _ancestral_start.val().samples(); } samplecnt_t ancestral_length_samples () const { return _ancestral_length.val().samples(); } timepos_t ancestral_start () const { return _ancestral_start.val(); } @@ -238,33 +210,8 @@ public: timepos_t sync_position () const; timepos_t adjust_to_sync (timepos_t const &) const; - - /* first_sample() is an alias; last_sample() just hides some math */ - - samplepos_t first_sample () const { return position().samples(); } - samplepos_t last_sample () const { return first_sample() + length_samples() - 1; } - - /** Return the earliest possible value of _position given the - * value of _start within the region's sources - */ - timepos_t earliest_possible_position () const; - /** Return the last possible value of _last_sample given the - * value of _startin the regions's sources - */ samplepos_t latest_possible_sample () const; - Temporal::TimeRange last_range () const { - return Temporal::TimeRange (last_position(), last_position() + _last_length); - } - - Temporal::TimeRange range_samples () const { - return Temporal::TimeRange (timepos_t (first_sample()), timepos_t (first_sample() + length_samples())); - } - - Temporal::TimeRange range () const { - return Temporal::TimeRange (position(), position() + length()); - } - bool hidden () const { return _hidden; } bool muted () const { return _muted; } bool opaque () const { return _opaque; } @@ -281,7 +228,6 @@ public: Trimmable::CanTrim can_trim () const; - Temporal::TimeDomain position_time_domain () const; void set_position_time_domain (Temporal::TimeDomain ps); void recompute_position_from_time_domain (); @@ -317,7 +263,6 @@ public: std::string source_string () const; - /* EDITING OPERATIONS */ void set_length (timecnt_t const &); @@ -365,34 +310,6 @@ public: void modify_front_unchecked (timepos_t const & new_position, bool reset_fade); void modify_end_unchecked (timepos_t const & new_position, bool reset_fade); - Temporal::timepos_t region_beats_to_absolute_time(Temporal::Beats beats) const; - /** Convert a timestamp in beats into timepos_t (both relative to region position) */ - Temporal::timepos_t region_beats_to_region_time (Temporal::Beats beats) const { - return timepos_t (position().distance (region_beats_to_absolute_time (beats))); - } - /** Convert a timestamp in beats relative to region position into beats relative to source start */ - Temporal::Beats region_beats_to_source_beats (Temporal::Beats beats) const { - return position().distance (region_beats_to_absolute_time (beats)).beats (); - } - /** Convert a distance within a region to beats relative to region position */ - Temporal::Beats region_distance_to_region_beats (Temporal::timecnt_t const &) const; - - /** Convert a timestamp in beats measured from source start into absolute beats */ - Temporal::Beats source_beats_to_absolute_beats(Temporal::Beats beats) const; - - /** Convert a timestamp in beats measured from source start into absolute samples */ - Temporal::timepos_t source_beats_to_absolute_time(Temporal::Beats beats) const; - - /** Convert a timestamp in beats measured from source start into region-relative samples */ - Temporal::timepos_t source_beats_to_region_time(Temporal::Beats beats) const { - return timepos_t (position().distance (source_beats_to_absolute_time (beats))); - } - /** Convert a timestamp in absolute time to beats measured from source start*/ - Temporal::Beats absolute_time_to_source_beats(Temporal::timepos_t const &) const; - - Temporal::Beats absolute_time_to_region_beats (Temporal::timepos_t const &) const; - - Temporal::timepos_t absolute_time_to_region_time (Temporal::timepos_t const &) const; int apply (Filter &, PBD::Progress* progress = 0); @@ -592,8 +509,6 @@ protected: PBD::Property _left_of_split; PBD::Property _right_of_split; PBD::Property _valid_transients; - PBD::Property _start; - PBD::Property _length; /** Sync position relative to the start of our file */ PBD::Property _sync_position; @@ -651,7 +566,6 @@ private: PBD::Property _reg_group; PBD::Property _contents; // type is irrelevant - timecnt_t _last_length; mutable RegionEditState _first_edit; layer_t _layer; diff --git a/libs/ardour/ardour/slice.h b/libs/ardour/ardour/slice.h new file mode 100644 index 0000000000..d5b05e41f4 --- /dev/null +++ b/libs/ardour/ardour/slice.h @@ -0,0 +1,122 @@ +#ifndef __libardour_slice_h__ +#define __libardour_slice_h__ + +#include "pbd/properties.h" +#include "pbd/stateful.h" + +#include "temporal/timeline.h" +#include "temporal/range.h" + +#include "ardour/libardour_visibility.h" +#include "ardour/types.h" +#include "ardour/types_convert.h" + +namespace ARDOUR { + +namespace Properties { + LIBARDOUR_API extern PBD::PropertyDescriptor start; + LIBARDOUR_API extern PBD::PropertyDescriptor length; +} + +class LIBARDOUR_API Slice : virtual public PBD::Stateful +{ + public: + Slice (Temporal::timepos_t const &, Temporal::timecnt_t const &); + Slice (Slice const &); + virtual ~Slice() {} + + Slice& operator= (Slice const &); + + timepos_t position () const { return _length.val().position(); } + timepos_t start () const { return _start.val(); } + timecnt_t length () const { return _length.val(); } + timepos_t end() const; + timepos_t nt_last() const { return end().decrement(); } + + /* these two are valid ONLY during a StateChanged signal handler */ + + timepos_t last_position () const { return _last_length.position(); } + timecnt_t last_length () const { return _last_length; } + + timepos_t source_position () const; + timecnt_t source_relative_position (Temporal::timepos_t const &) const; + timecnt_t region_relative_position (Temporal::timepos_t const &) const; + + samplepos_t position_sample () const { return position().samples(); } + samplecnt_t start_sample () const { return _start.val().samples(); } + samplecnt_t length_samples () const { return _length.val().samples(); } + + + /* first_sample() is an alias; last_sample() just hides some math */ + + samplepos_t first_sample () const { return position().samples(); } + samplepos_t last_sample () const { return first_sample() + length_samples() - 1; } + + /** Return the earliest possible value of _position given the + * value of _start within the region's sources + */ + timepos_t earliest_possible_position () const; + /** Return the last possible value of _last_sample given the + * value of _startin the regions's sources + */ + samplepos_t latest_possible_sample () const; + + Temporal::TimeRange last_range () const { + return Temporal::TimeRange (last_position(), last_position() + _last_length); + } + + Temporal::TimeRange range_samples () const { + return Temporal::TimeRange (timepos_t (first_sample()), timepos_t (first_sample() + length_samples())); + } + + Temporal::TimeRange range () const { + return Temporal::TimeRange (position(), position() + length()); + } + + Temporal::timepos_t region_beats_to_absolute_time(Temporal::Beats beats) const; + /** Convert a timestamp in beats into timepos_t (both relative to region position) */ + Temporal::timepos_t region_beats_to_region_time (Temporal::Beats beats) const { + return timepos_t (position().distance (region_beats_to_absolute_time (beats))); + } + /** Convert a timestamp in beats relative to region position into beats relative to source start */ + Temporal::Beats region_beats_to_source_beats (Temporal::Beats beats) const { + return position().distance (region_beats_to_absolute_time (beats)).beats (); + } + /** Convert a distance within a region to beats relative to region position */ + Temporal::Beats region_distance_to_region_beats (Temporal::timecnt_t const &) const; + + /** Convert a timestamp in beats measured from source start into absolute beats */ + Temporal::Beats source_beats_to_absolute_beats(Temporal::Beats beats) const; + + /** Convert a timestamp in beats measured from source start into absolute samples */ + Temporal::timepos_t source_beats_to_absolute_time(Temporal::Beats beats) const; + + /** Convert a timestamp in beats measured from source start into region-relative samples */ + Temporal::timepos_t source_beats_to_region_time(Temporal::Beats beats) const { + return timepos_t (position().distance (source_beats_to_absolute_time (beats))); + } + /** Convert a timestamp in absolute time to beats measured from source start*/ + Temporal::Beats absolute_time_to_source_beats(Temporal::timepos_t const &) const; + + Temporal::Beats absolute_time_to_region_beats (Temporal::timepos_t const &) const; + + Temporal::timepos_t absolute_time_to_region_time (Temporal::timepos_t const &) const; + + Temporal::TimeDomain position_time_domain () const; + + protected: + PBD::Property _start; + PBD::Property _length; + timecnt_t _last_length; + + virtual void set_length_internal (timecnt_t const &); + virtual void set_start_internal (timepos_t const &); + virtual void set_position_internal (timepos_t const &); + + private: + void register_properties (); +}; + +} /* namespace */ + +#endif /* __libardour_slice_h__ */ diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 9d6fea03cb..584be02b99 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -232,8 +232,6 @@ Region::register_properties () , _left_of_split (Properties::left_of_split, false) \ , _right_of_split (Properties::right_of_split, false) \ , _valid_transients (Properties::valid_transients, false) \ - , _start (Properties::start, (s)) \ - , _length (Properties::length, (l)) \ , _sync_position (Properties::sync_position, (s)) \ , _transient_user_start (0) \ , _transient_analysis_start (0) \ @@ -263,8 +261,6 @@ Region::register_properties () , _left_of_split (Properties::left_of_split, other->_left_of_split) \ , _right_of_split (Properties::right_of_split, other->_right_of_split) \ , _valid_transients (Properties::valid_transients, other->_valid_transients) \ - , _start(Properties::start, other->_start) \ - , _length(Properties::length, other->_length) \ , _sync_position(Properties::sync_position, other->_sync_position) \ , _user_transients (other->_user_transients) \ , _transient_user_start (other->_transient_user_start) \ @@ -294,11 +290,11 @@ Region::register_properties () /* derived-from-derived constructor (no sources in constructor) */ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, const string& name, DataType type) : SessionObject(s, name) + , Slice (start, length) , _type (type) , _fx_latency (0) , _fx_tail (0) , REGION_DEFAULT_STATE (start,length) - , _last_length (length) , _first_edit (EditChangesNothing) , _layer (0) , _changemap (0) @@ -311,12 +307,13 @@ Region::Region (Session& s, timepos_t const & start, timecnt_t const & length, c /** Basic Region constructor (many sources) */ Region::Region (const SourceList& srcs) : SessionObject(srcs.front()->session(), "toBeRenamed") + , Slice (_type == DataType::MIDI ? timepos_t (Temporal::Beats()) : timepos_t::from_superclock (0), + _type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0)) , _type (srcs.front()->type()) , _fx_latency (0) , _fx_tail (0) , REGION_DEFAULT_STATE(_type == DataType::MIDI ? timepos_t (Temporal::Beats()) : timepos_t::from_superclock (0), _type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0)) - , _last_length (_type == DataType::MIDI ? timecnt_t (Temporal::Beats()) : timecnt_t::from_superclock (0)) , _first_edit (EditChangesNothing) , _layer (0) , _changemap (0) @@ -332,11 +329,11 @@ Region::Region (const SourceList& srcs) /** Create a new Region from an existing one */ Region::Region (std::shared_ptr other) : SessionObject(other->session(), other->name()) + , Slice (*other.get()) , _type (other->data_type()) , _fx_latency (0) , _fx_tail (0) , REGION_COPY_STATE (other) - , _last_length (other->_last_length) , _first_edit (EditChangesNothing) , _layer (other->_layer) , _changemap (other->_changemap) @@ -392,11 +389,11 @@ Region::Region (std::shared_ptr other) */ Region::Region (std::shared_ptr other, timecnt_t const & offset) : SessionObject(other->session(), other->name()) + , Slice (*other.get()) , _type (other->data_type()) , _fx_latency (0) , _fx_tail (0) , REGION_COPY_STATE (other) - , _last_length (other->_last_length) , _first_edit (EditChangesNothing) , _layer (other->_layer) , _changemap (other->_changemap) @@ -439,11 +436,11 @@ Region::Region (std::shared_ptr other, timecnt_t const & offset) /** Create a copy of @p other but with different sources. Used by filters */ Region::Region (std::shared_ptr other, const SourceList& srcs) : SessionObject (other->session(), other->name()) + , Slice (*other.get()) , _type (srcs.front()->type()) , _fx_latency (0) , _fx_tail (0) , REGION_COPY_STATE (other) - , _last_length (other->_last_length) , _first_edit (EditChangesID) , _layer (other->_layer) , _changemap (other->_changemap) @@ -594,6 +591,12 @@ Region::maybe_uncopy () /* this does nothing but marked a semantic moment once upon a time */ } +void +Region::set_start_internal (timepos_t const & s) +{ + _start = s; +} + void Region::first_edit () { @@ -2208,22 +2211,6 @@ Region::is_compound () const return max_source_level() > 0; } -void -Region::set_start_internal (timepos_t const & s) -{ - _start = s; -} - -timepos_t -Region::earliest_possible_position () const -{ - if (start() > timecnt_t (position(), timepos_t())) { - return timepos_t::from_superclock (0); - } else { - return source_position(); - } -} - samplecnt_t Region::latest_possible_sample () const { @@ -2243,108 +2230,6 @@ Region::latest_possible_sample () const return (position() + minlen).samples() - 1; } -Temporal::TimeDomain -Region::position_time_domain() const -{ - return position().time_domain(); -} - -timepos_t -Region::end() const -{ - /* one day we might want to enforce _position, _start and _length (or - some combination thereof) all being in the same time domain. - */ - return _length.val().end(); -} - -timepos_t -Region::source_position () const -{ - /* this is the position of the start of the source, in absolute time */ - return position().earlier (_start.val()); -} - -Temporal::Beats -Region::region_distance_to_region_beats (timecnt_t const & region_relative_offset) const -{ - return timecnt_t (region_relative_offset, position()).beats (); -} - -Temporal::Beats -Region::source_beats_to_absolute_beats (Temporal::Beats beats) const -{ - /* since the return type must be beats, force source_position() to - beats before adding, rather than after. - */ - return source_position().beats() + beats; -} - -Temporal::Beats -Region::absolute_time_to_region_beats(timepos_t const & b) const -{ - return (position().distance (b)).beats () + start().beats(); -} - -Temporal::timepos_t -Region::absolute_time_to_region_time (timepos_t const & t) const -{ - return start() + position().distance (t); -} - -Temporal::timepos_t -Region::region_beats_to_absolute_time (Temporal::Beats beats) const -{ - return position() + timepos_t (beats); -} - -Temporal::timepos_t -Region::source_beats_to_absolute_time (Temporal::Beats beats) const -{ - /* return the time corresponding to `beats' relative to the start of - the source. The start of the source is an implied position given by - region->position - region->start aka ::source_position() - */ - return source_position() + timepos_t (beats); -} - -/** Calculate (time - source_position) in Beats - * - * Measure the distance between the absolute time and the position of - * the source start, in beats. The result is positive if time is later - * than source position. - * - * @param p is an absolute time - * @returns time offset from p to the region's source position as the origin in Beat units - */ -Temporal::Beats -Region::absolute_time_to_source_beats(timepos_t const& p) const -{ - return source_position().distance (p).beats(); -} - -/** Calculate (pos - source-position) - * - * @param p is an absolute time - * @returns time offset from p to the region's source position as the origin. - */ -timecnt_t -Region::source_relative_position (timepos_t const & p) const -{ - return source_position().distance (p); -} - -/** Calculate (p - region-position) - * - * @param p is an absolute time - * @returns the time offset using the region (timeline) position as origin - */ -timecnt_t -Region::region_relative_position (timepos_t const & p) const -{ - return position().distance (p); -} - Temporal::TimeDomain Region::time_domain() const { diff --git a/libs/ardour/slice.cc b/libs/ardour/slice.cc new file mode 100644 index 0000000000..55d5a6d20f --- /dev/null +++ b/libs/ardour/slice.cc @@ -0,0 +1,193 @@ +#include "ardour/slice.h" + +using namespace ARDOUR; +using namespace Temporal; + +Slice::Slice (timepos_t const & s, timecnt_t const & l) + : _start (Properties::start, s) + , _length (Properties::length, l) + , _last_length (l) +{ + register_properties (); +} + +Slice::Slice (Slice const & other) + : _start (Properties::start, other._start) + , _length (Properties::length, other._length) + , _last_length (other._last_length) +{ +} + +void +Slice::register_properties () +{ + add_property (_start); + add_property (_length); +} + +timepos_t +Slice::source_position () const +{ + /* this is the position of the start of the source, in absolute time */ + return position().earlier (_start.val()); +} + +Temporal::Beats +Slice::region_distance_to_region_beats (timecnt_t const & region_relative_offset) const +{ + return timecnt_t (region_relative_offset, position()).beats (); +} + +Temporal::Beats +Slice::source_beats_to_absolute_beats (Temporal::Beats beats) const +{ + /* since the return type must be beats, force source_position() to + beats before adding, rather than after. + */ + return source_position().beats() + beats; +} + +Temporal::Beats +Slice::absolute_time_to_region_beats(timepos_t const & b) const +{ + return (position().distance (b)).beats () + start().beats(); +} + +Temporal::timepos_t +Slice::absolute_time_to_region_time (timepos_t const & t) const +{ + return start() + position().distance (t); +} + +Temporal::timepos_t +Slice::region_beats_to_absolute_time (Temporal::Beats beats) const +{ + return position() + timepos_t (beats); +} + +Temporal::timepos_t +Slice::source_beats_to_absolute_time (Temporal::Beats beats) const +{ + /* return the time corresponding to `beats' relative to the start of + the source. The start of the source is an implied position given by + region->position - region->start aka ::source_position() + */ + return source_position() + timepos_t (beats); +} + +/** Calculate (time - source_position) in Beats + * + * Measure the distance between the absolute time and the position of + * the source start, in beats. The result is positive if time is later + * than source position. + * + * @param p is an absolute time + * @returns time offset from p to the region's source position as the origin in Beat units + */ +Temporal::Beats +Slice::absolute_time_to_source_beats(timepos_t const& p) const +{ + return source_position().distance (p).beats(); +} + +/** Calculate (pos - source-position) + * + * @param p is an absolute time + * @returns time offset from p to the region's source position as the origin. + */ +timecnt_t +Slice::source_relative_position (timepos_t const & p) const +{ + return source_position().distance (p); +} + +/** Calculate (p - region-position) + * + * @param p is an absolute time + * @returns the time offset using the region (timeline) position as origin + */ +timecnt_t +Slice::region_relative_position (timepos_t const & p) const +{ + return position().distance (p); +} + + +Temporal::TimeDomain +Slice::position_time_domain() const +{ + return position().time_domain(); +} + +timepos_t +Slice::end() const +{ + /* one day we might want to enforce _position, _start and _length (or + some combination thereof) all being in the same time domain. + */ + return _length.val().end(); +} + + +void +Slice::set_start_internal (timepos_t const & s) +{ + _start = s; +} + +void +Slice::set_length_internal (timecnt_t const & len) +{ + /* maintain position value of both _last_length and _length. + * + * This is very important: set_length() can only be used to the length + * component of _length, and set_position() can only be used to set the + * position component. + */ + + _last_length = timecnt_t (_length.val().distance(), _last_length.position()); + _length = timecnt_t (len.distance(), _length.val().position()); +} + +void +Slice::set_position_internal (timepos_t const & pos) +{ + if (position() == pos) { + return; + } + + /* We emit a change of Properties::length even if the position hasn't changed + * (see Slice::set_position), so we must always set this up so that + * e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position. + * + * maintain length value of both _last_length and _length. + * + * This is very important: set_length() can only be used to the length + * component of _length, and set_position() can only be used to set the + * position component. + */ + + _last_length.set_position (position()); + _length = timecnt_t (_length.val().distance(), pos); + + /* check that the new _position wouldn't make the current + * length impossible - if so, change the length. + * + * XXX is this the right thing to do? + */ + if (timepos_t::max (_length.val().time_domain()).earlier (_length) < position()) { + _last_length = _length; + _length = position().distance (timepos_t::max (position().time_domain())); + } +} + +timepos_t +Slice::earliest_possible_position () const +{ + if (start() > timecnt_t (position(), timepos_t())) { + return timepos_t::from_superclock (0); + } else { + return source_position(); + } +} + diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 6da16f8c45..ecefb1108b 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -234,6 +234,7 @@ libardour_sources = [ 'simple_export.cc', 'slavable.cc', 'slavable_automation_control.cc', + 'slice.cc', 'smf_source.cc', 'sndfile_helpers.cc', 'sndfileimportable.cc',