alter Source::_length from timecnt_t to timepos_t

THe length of a Source(File) is always measured from its start. In this sense,
the length is like a position on the timeline, which is a duration with an
implicit origin, or a Region start, also a duration with an implicit origin (in
that case the start of the Source). There is no good reason for using
a timecnt_t for this value, because the position component of a timecnt_t
(the origin for the duration) is implicit and always zero. So we make
this property into a timepos_t, and include a number of asserts() to check
for common possible coding errors related to the time domain
This commit is contained in:
Paul Davis 2022-01-28 15:52:27 -07:00
parent 8e749d7e76
commit c6a31250ba
13 changed files with 52 additions and 53 deletions

View File

@ -925,18 +925,20 @@ Editor::add_sources (vector<string> paths,
PropertyList plist;
/* Fudge region length to ensure it is non-zero; make it 1 beat at 120bpm
for want of a better idea. It can't be too small, otherwise if this
is a MIDI region the conversion from samples -> beats -> samples will
round it back down to 0 again.
for want of a better idea.
*/
timecnt_t len = (*x)->length ();
timepos_t len = (*x)->length ();
cerr << "for " << (*x)->name() << " source length appears to be " << len << endl;
if (len == 0) {
len = timecnt_t (_session->sample_rate ()) / 2;
if (len.is_zero()) {
if ((*x)->type() == DataType::AUDIO) {
len = timepos_t (_session->sample_rate ()) / 2;
} else {
len = timepos_t (Beats (1, 0));
}
cerr << " reset to use " << len << endl;
}
plist.add (ARDOUR::Properties::start, timecnt_t ((*x)->type() == DataType::AUDIO ? Temporal::AudioTime : Temporal::BeatTime));
plist.add (ARDOUR::Properties::start, timepos_t ((*x)->type() == DataType::AUDIO ? Temporal::AudioTime : Temporal::BeatTime));
plist.add (ARDOUR::Properties::length, len);
plist.add (ARDOUR::Properties::name, region_name);
plist.add (ARDOUR::Properties::layer, 0);

View File

@ -51,7 +51,7 @@ class LIBARDOUR_API AudioSource : virtual public Source, public ARDOUR::AudioRea
samplecnt_t readable_length_samples() const { return _length.samples(); }
virtual uint32_t n_channels() const { return 1; }
void update_length (timecnt_t const & cnt);
void update_length (timepos_t const & dur);
virtual samplecnt_t available_peaks (double zoom) const;

View File

@ -135,7 +135,7 @@ public:
void set_selected_for_solo(bool yn);
timecnt_t source_length (uint32_t n) const;
timepos_t source_length (uint32_t n) const;
uint32_t max_source_level () const;
/* these two are valid ONLY during a StateChanged signal handler */
@ -498,7 +498,6 @@ private:
void maybe_uncopy ();
bool verify_start (timepos_t const &);
bool verify_start_mutable (timecnt_t&);
bool verify_length (timecnt_t&);
virtual void recompute_at_start () = 0;

View File

@ -56,7 +56,7 @@ public:
void append_event_beats (const Lock& lock, const Evoral::Event<Temporal::Beats>& ev);
void append_event_samples (const Lock& lock, const Evoral::Event<samplepos_t>& ev, samplepos_t source_start);
void update_length (timecnt_t const & cnt);
void update_length (timepos_t const & dur);
void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
void mark_streaming_write_completed (const Lock& lock);

View File

@ -75,11 +75,10 @@ public:
time_t timestamp() const { return _timestamp; }
void stamp (time_t when) { _timestamp = when; }
virtual timecnt_t length() const { return _length; }
samplecnt_t length_samples () const { return _length.samples(); };
virtual timepos_t length() const { return _length; }
virtual bool empty () const;
virtual void update_length (timecnt_t const & cnt) {}
virtual void update_length (timepos_t const & dur) {}
void set_take_id (std::string id) { _take_id =id; }
const std::string& take_id () const { return _take_id; }
@ -161,7 +160,7 @@ public:
uint32_t _level; /* how deeply nested is this source w.r.t a disk file */
std::string _ancestor_name;
std::string _captured_for;
timecnt_t _length;
timepos_t _length;
XrunPositions _xruns;
CueMarkers _cue_markers;

View File

@ -43,8 +43,8 @@ public:
float sample_rate () const { return _session.nominal_sample_rate(); }
timepos_t natural_position() const { return _source->natural_position() * _ratio;}
samplecnt_t readable_length_samples() const { return _source->length_samples () * _ratio; }
timecnt_t length () const { return timecnt_t ((samplecnt_t) (_source->length_samples () * _ratio)); }
samplecnt_t readable_length_samples() const { assert (_source->length().time_domain() == Temporal::AudioTime); return _source->length().samples () * _ratio; }
timepos_t length () const { assert (_source->length().time_domain() == Temporal::AudioTime); return timepos_t ((samplepos_t) (_source->length().samples () * _ratio)); }
bool can_be_analysed() const { return false; }
bool clamped_at_unity() const { return false; }

View File

@ -150,10 +150,14 @@ AudioSource::set_state (const XMLNode& node, int /*version*/)
}
void
AudioSource::update_length (timecnt_t const & len)
AudioSource::update_length (timepos_t const & dur)
{
if (len > _length) {
_length = len;
assert (_length.time_domain() == dur.time_domain());
/* audio files cannot get smaller via this mechanism */
if (dur > _length) {
_length = dur;
}
}

View File

@ -471,7 +471,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
/* extend the length of the region to the end of a bar */
const Temporal::Beats length_beats = Temporal::Beats::ticks_at_rate(t, source->ppqn());
smfs->update_length (timecnt_t (length_beats.round_up_to_multiple(Temporal::Beats(pulses_per_bar,0)), timepos_t(Temporal::BeatTime)));
smfs->update_length (timepos_t (length_beats.round_up_to_multiple(Temporal::Beats(pulses_per_bar,0))));
smfs->mark_streaming_write_completed (source_lock);
smfs->load_model (source_lock, true);

View File

@ -1347,8 +1347,8 @@ Region::_set_state (const XMLNode& node, int version, PropertyChange& what_chang
*/
if (!_sources.empty() && _type == DataType::AUDIO) {
if ((length().time_domain() == Temporal::AudioTime) && (length() > _sources.front()->length())) {
_length = _sources.front()->length() - start();
if ((length().time_domain() == Temporal::AudioTime) && (length().distance() > _sources.front()->length())) {
_length = timecnt_t (start().distance (_sources.front()->length()), _length.val().position());
}
}
@ -1659,7 +1659,7 @@ Region::uses_source (boost::shared_ptr<const Source> source, bool shallow) const
}
timecnt_t
timepos_t
Region::source_length (uint32_t n) const
{
assert (n < _sources.size());
@ -1676,10 +1676,12 @@ Region::verify_length (timecnt_t& len)
timecnt_t maxlen;
for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - _start);
/* this is computing the distance between _start and the end of the source */
timecnt_t max_possible_length = _start.val().distance (source_length(n));
maxlen = max (maxlen, max_possible_length);
}
len = min (len, maxlen);
len = timecnt_t (min (len, maxlen), len.position());
return true;
}
@ -1694,7 +1696,7 @@ Region::verify_start_and_length (timepos_t const & new_start, timecnt_t& new_len
timecnt_t maxlen;
for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - new_start);
maxlen = max (maxlen, new_start.distance (source_length(n)));
}
new_length = min (new_length, maxlen);
@ -1710,29 +1712,14 @@ Region::verify_start (timepos_t const & pos)
}
for (uint32_t n = 0; n < _sources.size(); ++n) {
if (pos > source_length(n) - _length) {
/* _start can't be before the start of the region as defined by its length */
if (pos > source_length(n).earlier (_length)) {
return false;
}
}
return true;
}
bool
Region::verify_start_mutable (timecnt_t & new_start)
{
if (source() && source()->length_mutable()) {
return true;
}
for (uint32_t n = 0; n < _sources.size(); ++n) {
if (new_start > source_length(n) - _length) {
new_start = source_length(n) - _length;
}
}
return true;
}
boost::shared_ptr<Region>
Region::get_parent() const
{
@ -1977,7 +1964,7 @@ Region::latest_possible_sample () const
/* non-audio regions have a length that may vary based on their
* position, so we have to pass it in the call.
*/
minlen = min (minlen, (*i)->length ());
minlen = min (minlen, timecnt_t ((*i)->length (), (*i)->natural_position()));
}
/* the latest possible last sample is determined by the current

View File

@ -395,9 +395,10 @@ SMFSource::write_unlocked (const Lock& lock,
}
void
SMFSource::update_length (timecnt_t const & cnt)
SMFSource::update_length (timepos_t const & dur)
{
_length = cnt;
assert (!_length || (_length.time_domain() == dur.time_domain()));
_length = dur;
}
/** Append an event with a timestamp in beats */
@ -445,7 +446,8 @@ SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock,
_model->append (ev, event_id);
}
_length = max (_length, timecnt_t (time));
assert (!_length || (_length.time_domain() == Temporal::BeatTime));
_length = timepos_t (max (_length.beats(), time));
const Temporal::Beats delta_time_beats = time - _last_ev_time_beats;
const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn());
@ -498,7 +500,8 @@ SMFSource::append_event_samples (const Glib::Threads::Mutex::Lock& lock,
_model->append (beat_ev, event_id);
}
_length = max (_length, timecnt_t (ev_time_beats, timepos_t (position)));
assert (!_length || (_length.time_domain() == Temporal::BeatTime));
_length = timepos_t (max (_length.beats(), ev_time_beats));
/* a distance measure that starts at @param _last_ev_time_samples (audio time) and
extends for ev.time() (audio time)
@ -718,7 +721,8 @@ SMFSource::load_model (const Glib::Threads::Mutex::Lock& lock, bool force_reload
scratch_size = std::max(size, scratch_size);
size = scratch_size;
_length = max (_length, timecnt_t (event_time));
assert (!_length || (_length.time_domain() == Temporal::BeatTime));
_length = max (_length, timepos_t (event_time));
}
/* event ID's must immediately precede the event they are for */

View File

@ -605,13 +605,15 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, samplecnt_t cnt)
return 0;
}
assert (_length.time_domain() == Temporal::AudioTime);
samplepos_t sample_pos = _length.samples();
if (write_float (data, sample_pos, cnt) != cnt) {
return 0;
}
update_length (_length + timecnt_t (cnt, timepos_t (Temporal::AudioTime)));
assert (_length.time_domain() == Temporal::AudioTime);
update_length (timepos_t (_length.samples() + cnt));
if (_build_peakfiles) {
compute_and_write_peaks (data, sample_pos, cnt, true, true);

View File

@ -419,7 +419,6 @@ Source::set_natural_position (timepos_t const & pos)
{
_natural_position = pos;
_have_natural_position = true;
_length.set_position (pos);
}
void
@ -533,7 +532,7 @@ Source::clear_cue_markers ()
bool
Source::empty () const
{
return _length == timecnt_t();
return _length == timepos_t ();
}
bool

View File

@ -74,6 +74,7 @@ class LIBTEMPORAL_API timepos_t : public int62_t {
bool is_positive () const { return val() > 0; }
bool is_negative () const { return val() < 0; }
bool is_zero () const { return val() == 0; }
bool operator! () const { return val() == 0; }
Temporal::TimeDomain time_domain () const { if (flagged()) return Temporal::BeatTime; return Temporal::AudioTime; }
void set_time_domain (Temporal::TimeDomain);
@ -84,6 +85,8 @@ class LIBTEMPORAL_API timepos_t : public int62_t {
Beats beats() const { if (is_beats()) return Beats::ticks (val()); return _beats (); }
timepos_t & operator= (timecnt_t const & t); /* will throw() if val is negative */
timepos_t & operator= (Beats const & b) { v.store (build (true, b.to_ticks())); return *this; }
timepos_t & operator= (samplepos_t const & s) { v.store (build (false, samples_to_superclock (s, TEMPORAL_SAMPLE_RATE))); return *this; }
timepos_t operator-() const { return timepos_t (int62_t::operator-()); }