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; PropertyList plist;
/* Fudge region length to ensure it is non-zero; make it 1 beat at 120bpm /* 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 for want of a better idea.
is a MIDI region the conversion from samples -> beats -> samples will
round it back down to 0 again.
*/ */
timecnt_t len = (*x)->length (); timepos_t len = (*x)->length ();
cerr << "for " << (*x)->name() << " source length appears to be " << len << endl; cerr << "for " << (*x)->name() << " source length appears to be " << len << endl;
if (len == 0) { if (len.is_zero()) {
len = timecnt_t (_session->sample_rate ()) / 2; 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; 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::length, len);
plist.add (ARDOUR::Properties::name, region_name); plist.add (ARDOUR::Properties::name, region_name);
plist.add (ARDOUR::Properties::layer, 0); 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(); } samplecnt_t readable_length_samples() const { return _length.samples(); }
virtual uint32_t n_channels() const { return 1; } 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; virtual samplecnt_t available_peaks (double zoom) const;

View file

@ -135,7 +135,7 @@ public:
void set_selected_for_solo(bool yn); 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; uint32_t max_source_level () const;
/* these two are valid ONLY during a StateChanged signal handler */ /* these two are valid ONLY during a StateChanged signal handler */
@ -498,7 +498,6 @@ private:
void maybe_uncopy (); void maybe_uncopy ();
bool verify_start (timepos_t const &); bool verify_start (timepos_t const &);
bool verify_start_mutable (timecnt_t&);
bool verify_length (timecnt_t&); bool verify_length (timecnt_t&);
virtual void recompute_at_start () = 0; 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_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 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_midi_write_started (const Lock& lock, NoteMode mode);
void mark_streaming_write_completed (const Lock& lock); void mark_streaming_write_completed (const Lock& lock);

View file

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

View file

@ -43,8 +43,8 @@ public:
float sample_rate () const { return _session.nominal_sample_rate(); } float sample_rate () const { return _session.nominal_sample_rate(); }
timepos_t natural_position() const { return _source->natural_position() * _ratio;} timepos_t natural_position() const { return _source->natural_position() * _ratio;}
samplecnt_t readable_length_samples() const { return _source->length_samples () * _ratio; } samplecnt_t readable_length_samples() const { assert (_source->length().time_domain() == Temporal::AudioTime); return _source->length().samples () * _ratio; }
timecnt_t length () const { return timecnt_t ((samplecnt_t) (_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 can_be_analysed() const { return false; }
bool clamped_at_unity() 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 void
AudioSource::update_length (timecnt_t const & len) AudioSource::update_length (timepos_t const & dur)
{ {
if (len > _length) { assert (_length.time_domain() == dur.time_domain());
_length = len;
/* 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 */ /* 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()); 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->mark_streaming_write_completed (source_lock);
smfs->load_model (source_lock, true); 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 (!_sources.empty() && _type == DataType::AUDIO) {
if ((length().time_domain() == Temporal::AudioTime) && (length() > _sources.front()->length())) { if ((length().time_domain() == Temporal::AudioTime) && (length().distance() > _sources.front()->length())) {
_length = _sources.front()->length() - start(); _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 Region::source_length (uint32_t n) const
{ {
assert (n < _sources.size()); assert (n < _sources.size());
@ -1676,10 +1676,12 @@ Region::verify_length (timecnt_t& len)
timecnt_t maxlen; timecnt_t maxlen;
for (uint32_t n = 0; n < _sources.size(); ++n) { 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; return true;
} }
@ -1694,7 +1696,7 @@ Region::verify_start_and_length (timepos_t const & new_start, timecnt_t& new_len
timecnt_t maxlen; timecnt_t maxlen;
for (uint32_t n = 0; n < _sources.size(); ++n) { 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); 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) { 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 false;
} }
} }
return true; 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> boost::shared_ptr<Region>
Region::get_parent() const 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 /* non-audio regions have a length that may vary based on their
* position, so we have to pass it in the call. * 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 /* the latest possible last sample is determined by the current

View file

@ -395,9 +395,10 @@ SMFSource::write_unlocked (const Lock& lock,
} }
void 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 */ /** 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); _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 Temporal::Beats delta_time_beats = time - _last_ev_time_beats;
const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn()); 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); _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 /* a distance measure that starts at @param _last_ev_time_samples (audio time) and
extends for ev.time() (audio time) 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); scratch_size = std::max(size, scratch_size);
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 */ /* 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; return 0;
} }
assert (_length.time_domain() == Temporal::AudioTime);
samplepos_t sample_pos = _length.samples(); samplepos_t sample_pos = _length.samples();
if (write_float (data, sample_pos, cnt) != cnt) { if (write_float (data, sample_pos, cnt) != cnt) {
return 0; 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) { if (_build_peakfiles) {
compute_and_write_peaks (data, sample_pos, cnt, true, true); 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; _natural_position = pos;
_have_natural_position = true; _have_natural_position = true;
_length.set_position (pos);
} }
void void
@ -533,7 +532,7 @@ Source::clear_cue_markers ()
bool bool
Source::empty () const Source::empty () const
{ {
return _length == timecnt_t(); return _length == timepos_t ();
} }
bool bool

View file

@ -74,6 +74,7 @@ class LIBTEMPORAL_API timepos_t : public int62_t {
bool is_positive () const { return val() > 0; } bool is_positive () const { return val() > 0; }
bool is_negative () const { return val() < 0; } bool is_negative () const { return val() < 0; }
bool is_zero () 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; } Temporal::TimeDomain time_domain () const { if (flagged()) return Temporal::BeatTime; return Temporal::AudioTime; }
void set_time_domain (Temporal::TimeDomain); 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 (); } 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= (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-()); } timepos_t operator-() const { return timepos_t (int62_t::operator-()); }