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.
This commit is contained in:
Paul Davis 2023-12-29 09:15:11 -07:00
parent 94b051e318
commit f3da2cfd8b
5 changed files with 330 additions and 213 deletions

View File

@ -41,6 +41,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"
@ -67,8 +68,6 @@ namespace Properties {
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> hidden;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> position_locked;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> valid_transients; // used for signal only
LIBARDOUR_API extern PBD::PropertyDescriptor<timepos_t> start;
LIBARDOUR_API extern PBD::PropertyDescriptor<timecnt_t> length;
LIBARDOUR_API extern PBD::PropertyDescriptor<timepos_t> sync_position;
LIBARDOUR_API extern PBD::PropertyDescriptor<layer_t> layer;
LIBARDOUR_API extern PBD::PropertyDescriptor<timepos_t> ancestral_start;
@ -101,6 +100,7 @@ enum LIBARDOUR_API RegionOperationFlag {
class LIBARDOUR_API Region
: public SessionObject
, public std::enable_shared_from_this<Region>
, public Slice
, public Trimmable
, public Movable
, public Temporal::TimeDomainSwapper
@ -126,27 +126,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(); }
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);
@ -154,11 +133,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(); }
@ -232,33 +206,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; }
@ -275,7 +224,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 ();
@ -311,7 +259,6 @@ public:
std::string source_string () const;
/* EDITING OPERATIONS */
void set_length (timecnt_t const &);
@ -359,34 +306,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);
@ -550,8 +469,6 @@ protected:
PBD::Property<bool> _left_of_split;
PBD::Property<bool> _right_of_split;
PBD::Property<bool> _valid_transients;
PBD::Property<timepos_t> _start;
PBD::Property<timecnt_t> _length;
/** Sync position relative to the start of our file */
PBD::Property<timepos_t> _sync_position;
@ -609,7 +526,6 @@ private:
PBD::Property<uint64_t> _reg_group;
PBD::Property<bool> _contents; // type is irrelevant
timecnt_t _last_length;
mutable RegionEditState _first_edit;
layer_t _layer;

122
libs/ardour/ardour/slice.h Normal file
View File

@ -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<timepos_t> start;
LIBARDOUR_API extern PBD::PropertyDescriptor<timecnt_t> 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<timepos_t> _start;
PBD::Property<timecnt_t> _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__ */

View File

@ -227,8 +227,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) \
@ -258,8 +256,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) \
@ -289,9 +285,9 @@ 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)
, REGION_DEFAULT_STATE (start,length)
, _last_length (length)
, _first_edit (EditChangesNothing)
, _layer (0)
, _changemap (0)
@ -304,10 +300,11 @@ 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())
, 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)
@ -325,9 +322,9 @@ Region::Region (const SourceList& srcs)
/** Create a new Region from an existing one */
Region::Region (std::shared_ptr<const Region> other)
: SessionObject(other->session(), other->name())
, Slice (*other.get())
, _type (other->data_type())
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesNothing)
, _layer (other->_layer)
, _changemap (other->_changemap)
@ -383,9 +380,9 @@ Region::Region (std::shared_ptr<const Region> other)
*/
Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
: SessionObject(other->session(), other->name())
, Slice (*other.get())
, _type (other->data_type())
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesNothing)
, _layer (other->_layer)
, _changemap (other->_changemap)
@ -428,9 +425,9 @@ Region::Region (std::shared_ptr<const Region> other, timecnt_t const & offset)
/** Create a copy of @p other but with different sources. Used by filters */
Region::Region (std::shared_ptr<const Region> other, const SourceList& srcs)
: SessionObject (other->session(), other->name())
, Slice (*other.get())
, _type (srcs.front()->type())
, REGION_COPY_STATE (other)
, _last_length (other->_last_length)
, _first_edit (EditChangesID)
, _layer (other->_layer)
, _changemap (other->_changemap)
@ -581,6 +578,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 ()
{
@ -2151,22 +2154,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
{
@ -2186,108 +2173,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
{

193
libs/ardour/slice.cc Normal file
View File

@ -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();
}
}

View File

@ -233,6 +233,7 @@ libardour_sources = [
'simple_export.cc',
'slavable.cc',
'slavable_automation_control.cc',
'slice.cc',
'smf_source.cc',
'sndfile_helpers.cc',
'sndfileimportable.cc',