add new sample_aligned_superclock() method and use it when converting Beats to superclock

There can never be fractional audio time, and since superclock_t is used to represent audio time, when we
convert from Beat time, we should never, ever return a superclock value that does not correspond to
an integer number of samples.

This fixes a number of bugs, including any use of ARDOUR::Filter which writes a new (audio) file
to disk that must be an integer number of samples long, but may be derived from and later used
by a region that had an audio-time duration that is slightly longer (less than one sample)
than the audio file
This commit is contained in:
Paul Davis 2025-01-29 17:24:12 -07:00
parent 99ba22a4a4
commit 12ed9899ee
2 changed files with 38 additions and 33 deletions

View file

@ -513,14 +513,15 @@ TempoPoint::superclock_at (Temporal::Beats const & qn) const
TEMPO_MAP_ASSERT (qn >= _quarters); TEMPO_MAP_ASSERT (qn >= _quarters);
} }
superclock_t r;
if (!actually_ramped()) { if (!actually_ramped()) {
/* not ramped, use linear */ /* not ramped, use linear */
const Beats delta = qn - _quarters; const Beats delta = qn - _quarters;
const superclock_t spqn = superclocks_per_quarter_note (); const superclock_t spqn = superclocks_per_quarter_note ();
return _sclock + (spqn * delta.get_beats()) + muldiv_round (spqn, delta.get_ticks(), superclock_t (Temporal::ticks_per_beat)); r = _sclock + (spqn * delta.get_beats()) + muldiv_round (spqn, delta.get_ticks(), superclock_t (Temporal::ticks_per_beat));
} } else {
superclock_t r;
const double log_expr = superclocks_per_quarter_note() * _omega * DoubleableBeats (qn - _quarters).to_double(); const double log_expr = superclocks_per_quarter_note() * _omega * DoubleableBeats (qn - _quarters).to_double();
// std::cerr << "logexpr " << log_expr << " from " << superclocks_per_quarter_note() << " * " << _omega << " * " << (qn - _quarters) << std::endl; // std::cerr << "logexpr " << log_expr << " from " << superclocks_per_quarter_note() << " * " << _omega << " * " << (qn - _quarters) << std::endl;
@ -554,8 +555,11 @@ TempoPoint::superclock_at (Temporal::Beats const & qn) const
abort (); abort ();
} }
} }
}
return r; /* Now round up to the nearest sample-equivalent superclock value */
return sample_aligned_superclock (r, TEMPORAL_SAMPLE_RATE);
} }
superclock_t superclock_t

View file

@ -48,6 +48,7 @@ static inline superclock_t superclock_ticks_per_second() { return _superclock_ti
static inline superclock_t superclock_to_samples (superclock_t s, int sr) { return PBD::muldiv_floor (s, sr, superclock_ticks_per_second()); } static inline superclock_t superclock_to_samples (superclock_t s, int sr) { return PBD::muldiv_floor (s, sr, superclock_ticks_per_second()); }
static inline superclock_t samples_to_superclock (int64_t samples, int sr) { return PBD::muldiv_round (samples, superclock_ticks_per_second(), superclock_t (sr)); } static inline superclock_t samples_to_superclock (int64_t samples, int sr) { return PBD::muldiv_round (samples, superclock_ticks_per_second(), superclock_t (sr)); }
static inline superclock_t sample_aligned_superclock (superclock_t s, int sr) { return PBD::muldiv_floor (s, sr, sr); }
LIBTEMPORAL_API extern int most_recent_engine_sample_rate; LIBTEMPORAL_API extern int most_recent_engine_sample_rate;