From 3e8f4d80fb8955ad0bc4575d75cc93b022ec404b Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 18 Dec 2020 19:05:29 -0700 Subject: [PATCH] remove (almost) all floating point operations from Beats, add DoubleableBeats The latter is a "wrapper" around Beats that provides ::to_double() for those times when we absolutely need a floating point representation --- libs/temporal/tempo.cc | 6 +- libs/temporal/temporal/beats.h | 106 +++++++-------------------------- 2 files changed, 25 insertions(+), 87 deletions(-) diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index fc8f5438fc..a3c4247776 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -422,9 +422,9 @@ TempoPoint::compute_omega (samplecnt_t sr, superclock_t end_scpqn, Temporal::Bea return; } - _omega = ((1.0/end_scpqn) - (1.0/superclocks_per_quarter_note())) / quarter_duration.to_double(); + _omega = ((1.0/end_scpqn) - (1.0/superclocks_per_quarter_note())) / DoubleableBeats (quarter_duration).to_double(); - DEBUG_TRACE (DEBUG::TemporalMap, string_compose ("computed omega = %1%2 dur was %3\n", std::setprecision(12),_omega, quarter_duration.to_double())); + DEBUG_TRACE (DEBUG::TemporalMap, string_compose ("computed omega = %1%2 dur was %3\n", std::setprecision(12),_omega, DoubleableBeats (quarter_duration).to_double())); } superclock_t @@ -440,7 +440,7 @@ TempoPoint::superclock_at (Temporal::Beats const & qn) const return (spqn * qn.get_beats()) + int_div_round ((spqn * qn.get_ticks()), superclock_t (Temporal::ticks_per_beat)); } - return _sclock + llrint (log1p (superclocks_per_quarter_note() * _omega * (qn - _quarters).to_double()) / _omega); + return _sclock + llrint (log1p (superclocks_per_quarter_note() * _omega * DoubleableBeats (qn - _quarters).to_double()) / _omega); } superclock_t diff --git a/libs/temporal/temporal/beats.h b/libs/temporal/temporal/beats.h index 3ba67d4e73..f4bf005933 100644 --- a/libs/temporal/temporal/beats.h +++ b/libs/temporal/temporal/beats.h @@ -157,6 +157,10 @@ public: return *this; } + Beats snap_to (Temporal::Beats const & snap) const { + return (*this / snap) * snap; + } + Beats round_to_beat() const { return (_ticks >= (PPQN/2)) ? Beats (_beats + 1, 0) : Beats (_beats, 0); } @@ -182,11 +186,6 @@ public: Beats round_to_subdivision (int subdivision, RoundMode dir) const; - Beats snap_to (Temporal::Beats const & snap) const { - const double snap_time = snap.to_double(); - return Beats::from_double (ceil(to_double() / snap_time) * snap_time); - } - Beats abs () const { return Beats (::abs (_beats), ::abs (_ticks)); } @@ -202,11 +201,6 @@ public: return _beats == b._beats && _ticks == b._ticks; } - inline bool operator==(double t) const { - /* Acceptable tolerance is 1 tick. */ - return fabs(to_double() - t) <= (1.0 / PPQN); - } - inline bool operator==(int beats) const { return _beats == beats; } @@ -231,34 +225,6 @@ public: return _beats > b._beats || (_beats == b._beats && _ticks >= b._ticks); } - inline bool operator<(double b) const { - /* Acceptable tolerance is 1 tick. */ - const double time = to_double(); - if (fabs(time - b) <= (1.0 / PPQN)) { - return false; /* Effectively identical. */ - } else { - return time < b; - } - } - - inline bool operator<=(double b) const { - return operator==(b) || operator<(b); - } - - inline bool operator>(double b) const { - /* Acceptable tolerance is 1 tick. */ - const double time = to_double(); - if (fabs(time - b) <= (1.0 / PPQN)) { - return false; /* Effectively identical. */ - } else { - return time > b; - } - } - - inline bool operator>=(double b) const { - return operator==(b) || operator>(b); - } - Beats operator+(const Beats& b) const { return Beats(_beats + b._beats, _ticks + b._ticks); } @@ -267,32 +233,6 @@ public: return Beats(_beats - b._beats, _ticks - b._ticks); } - Beats operator+(double d) const { - return Beats(to_double() + d); - } - - Beats operator-(double d) const { - return Beats(to_double() - d); - } - - Beats operator+(int b) const { - return Beats (_beats + b, _ticks); - } - - Beats operator-(int b) const { - return Beats (_beats - b, _ticks); - } - - Beats& operator+=(int b) { - _beats += b; - return *this; - } - - Beats& operator-=(int b) { - _beats -= b; - return *this; - } - Beats operator-() const { /* must avoid normalization here, which will convert a negative value into a valid beat position before zero, which is not @@ -317,10 +257,12 @@ public: _ticks = B._ticks; return *this; - /* avoids calling ::to_double() to compute ratios of two Beat distances - */ - double operator/ (Beats const & other) { - return (double) to_ticks() / (double) other.to_ticks(); + Beats operator/ (Beats const & other) const { + return Beats::ticks (int_div_round (to_ticks(), other.to_ticks())); + } + + Beats operator* (Beats const & other) const { + return Beats::ticks (to_ticks () * other.to_ticks()); } Beats& operator+=(const Beats& b) { @@ -342,25 +284,10 @@ public: static Beats one_tick() { return Beats(0, 1); } -private: + protected: int32_t _beats; int32_t _ticks; - /* almost nobody should ever be allowed to use this method */ - friend class TempoPoint; - friend class ARDOUR::Track; - friend class ARDOUR::Variant; - friend class ARDOUR::MidiStretch; - friend class ARDOUR::MidiModel; - friend class ARDOUR::AutomationList; - friend class ARDOUR::MidiSource; - friend class ARDOUR::MidiRegion; - friend class ARDOUR::Quantize; - friend class ::QuantizeDialog; - friend class ::NoteDrag; - friend class ::NoteCreateDrag; - double to_double() const { return (double)_beats + (_ticks / (double)PPQN); } - /* this needs to exist because Evoral::Sequence is templated, and some * other possible template types cannot provide ::from_double */ @@ -375,6 +302,17 @@ private: } }; +/* Only contexts that really, absolutely need a floating point representation + * of a Beats value should ever use this. + */ + +class DoubleableBeats : public Beats +{ + public: + DoubleableBeats (Beats const & b) : Beats (b) {} + double to_double() const { return (double)_beats + (_ticks / (double)PPQN); } +}; + /* TIL, several horrible hours later, that sometimes the compiler looks in the namespace of a type (Temporal::Beats in this case) for an operator, and