convert internal implementation of Temporal::Beats to just use a 64 bit tick value
This expands significantly the maximum number of Beats that can be represented, which is a good thing in itself. It slightly speeds up some Beats::operator methods, and slightly slows down ::get_beats() and ::get_ticks(). One minor change in an API user was required, and several tweaks to the unit tests due to the macros being used by cppunit creating possible type confusion. Units test pass
This commit is contained in:
parent
4661957091
commit
696e05c3e5
@ -662,7 +662,7 @@ TempoMetric::bbt_at (timepos_t const & pos) const
|
||||
the current meter, which we'll call "grid"
|
||||
*/
|
||||
|
||||
const int64_t note_value_count = int_div_round (dq.get_beats() * _meter->note_value(), 4);
|
||||
const int64_t note_value_count = int_div_round (dq.get_beats() * _meter->note_value(), int64_t (4));
|
||||
|
||||
/* now construct a BBT_Offset using the count in grid units */
|
||||
|
||||
|
@ -66,45 +66,11 @@ class /*LIBTEMPORAL_API*/ Beats {
|
||||
public:
|
||||
LIBTEMPORAL_API static const int32_t PPQN = Temporal::ticks_per_beat;
|
||||
|
||||
Beats() : _beats(0), _ticks(0) {}
|
||||
Beats(const Beats& other) : _beats(other._beats), _ticks(other._ticks) {}
|
||||
|
||||
/** Normalize so ticks is within PPQN. */
|
||||
void normalize() {
|
||||
// First, fix negative ticks with positive beats
|
||||
while (_beats > 0 && _ticks < 0) {
|
||||
--_beats;
|
||||
_ticks += PPQN;
|
||||
}
|
||||
|
||||
// Now fix positive ticks with negative beats
|
||||
while (_beats < 0 && _ticks > 0) {
|
||||
++_beats;
|
||||
_ticks -= PPQN;
|
||||
}
|
||||
|
||||
assert ((_beats < 0 && _ticks <= 0) || (_beats > 0 && _ticks >= 0) || _beats == 0);
|
||||
|
||||
// Work with positive beats and ticks to normalize
|
||||
const int32_t sign = _beats < 0 ? -1 : _ticks < 0 ? -1 : 1;
|
||||
int32_t beats = ::abs(_beats);
|
||||
int32_t ticks = ::abs(_ticks);
|
||||
|
||||
// Fix ticks greater than 1 beat
|
||||
while (ticks >= PPQN) {
|
||||
++beats;
|
||||
ticks -= PPQN;
|
||||
}
|
||||
|
||||
// Set fields with appropriate sign
|
||||
_beats = sign * beats;
|
||||
_ticks = sign * ticks;
|
||||
}
|
||||
Beats() : _ticks(0) {}
|
||||
Beats(const Beats& other) : _ticks(other._ticks) {}
|
||||
|
||||
/** Create from a precise beats:ticks pair. */
|
||||
explicit Beats(int32_t b, int32_t t) : _beats(b), _ticks(t) {
|
||||
normalize();
|
||||
}
|
||||
explicit Beats(int32_t b, int32_t t) : _ticks ((b*PPQN) + t) {}
|
||||
|
||||
/** Create from a real number of beats. */
|
||||
static Beats from_double (double beats) {
|
||||
@ -120,8 +86,7 @@ public:
|
||||
|
||||
/** Create from ticks at the standard PPQN. */
|
||||
static Beats ticks(int64_t ticks) {
|
||||
assert (ticks/PPQN < std::numeric_limits<int32_t>::max());
|
||||
return Beats (ticks / PPQN, ticks % PPQN);
|
||||
return Beats (0, ticks);
|
||||
}
|
||||
|
||||
/** Create from ticks at a given rate.
|
||||
@ -135,25 +100,18 @@ public:
|
||||
return Beats(ticks / ppqn, (ticks % ppqn) * PPQN / ppqn);
|
||||
}
|
||||
|
||||
static int64_t make_ticks (Beats const & b) { return b.get_beats() * ticks_per_beat + b.get_ticks(); }
|
||||
int64_t to_ticks () const { return _ticks; }
|
||||
int64_t to_ticks (uint32_t ppqn) const { return (_ticks * ppqn) / PPQN; }
|
||||
|
||||
int64_t to_ticks() const { return (int64_t)_beats * PPQN + _ticks; }
|
||||
int64_t to_ticks(uint32_t ppqn) const { return (int64_t)_beats * ppqn + (_ticks * ppqn / PPQN); }
|
||||
|
||||
int32_t get_beats() const { return _beats; }
|
||||
int32_t get_ticks() const { return _ticks; }
|
||||
int64_t get_beats () const { return _ticks / PPQN; }
|
||||
int32_t get_ticks () const { return _ticks % PPQN; }
|
||||
|
||||
Beats& operator=(double time) {
|
||||
double whole;
|
||||
const double frac = modf(time, &whole);
|
||||
|
||||
_beats = whole;
|
||||
_ticks = frac * PPQN;
|
||||
*this = from_double (time);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Beats& operator=(const Beats& other) {
|
||||
_beats = other._beats;
|
||||
_ticks = other._ticks;
|
||||
return *this;
|
||||
}
|
||||
@ -170,32 +128,32 @@ public:
|
||||
}
|
||||
|
||||
Beats round_to_beat() const {
|
||||
return (_ticks >= (PPQN/2)) ? Beats (_beats + 1, 0) : Beats (_beats, 0);
|
||||
return (get_ticks() >= (PPQN/2)) ? Beats (get_beats() + 1, 0) : Beats (get_beats(), 0);
|
||||
}
|
||||
|
||||
Beats round_up_to_beat() const {
|
||||
return (_ticks == 0) ? *this : Beats(_beats + 1, 0);
|
||||
return (get_ticks() == 0) ? *this : Beats(get_beats() + 1, 0);
|
||||
}
|
||||
|
||||
Beats round_down_to_beat() const {
|
||||
return Beats(_beats, 0);
|
||||
return Beats(get_beats(), 0);
|
||||
}
|
||||
|
||||
|
||||
Beats prev_beat() const {
|
||||
/* always moves backwards even if currently on beat */
|
||||
return Beats (_beats-1, 0);
|
||||
return Beats (get_beats()-1, 0);
|
||||
}
|
||||
|
||||
Beats next_beat() const {
|
||||
/* always moves forwards even if currently on beat */
|
||||
return Beats (_beats+1, 0);
|
||||
return Beats (get_beats()+1, 0);
|
||||
}
|
||||
|
||||
LIBTEMPORAL_API Beats round_to_subdivision (int subdivision, RoundMode dir) const;
|
||||
|
||||
Beats abs () const {
|
||||
return Beats (::abs (_beats), ::abs (_ticks));
|
||||
return ticks (::abs (_ticks));
|
||||
}
|
||||
|
||||
Beats diff (Beats const & other) const {
|
||||
@ -206,11 +164,11 @@ public:
|
||||
}
|
||||
|
||||
inline bool operator==(const Beats& b) const {
|
||||
return _beats == b._beats && _ticks == b._ticks;
|
||||
return _ticks == b._ticks;
|
||||
}
|
||||
|
||||
inline bool operator==(int beats) const {
|
||||
return _beats == beats;
|
||||
return get_beats() == beats;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Beats& b) const {
|
||||
@ -218,38 +176,31 @@ public:
|
||||
}
|
||||
|
||||
inline bool operator<(const Beats& b) const {
|
||||
return _beats < b._beats || (_beats == b._beats && _ticks < b._ticks);
|
||||
return _ticks < b._ticks;
|
||||
}
|
||||
|
||||
inline bool operator<=(const Beats& b) const {
|
||||
return _beats < b._beats || (_beats == b._beats && _ticks <= b._ticks);
|
||||
return _ticks <= b._ticks;
|
||||
}
|
||||
|
||||
inline bool operator>(const Beats& b) const {
|
||||
return _beats > b._beats || (_beats == b._beats && _ticks > b._ticks);
|
||||
return _ticks > b._ticks;
|
||||
}
|
||||
|
||||
inline bool operator>=(const Beats& b) const {
|
||||
return _beats > b._beats || (_beats == b._beats && _ticks >= b._ticks);
|
||||
return _ticks >= b._ticks;
|
||||
}
|
||||
|
||||
Beats operator+(const Beats& b) const {
|
||||
return Beats(_beats + b._beats, _ticks + b._ticks);
|
||||
return ticks (_ticks + b._ticks);
|
||||
}
|
||||
|
||||
Beats operator-(const Beats& b) const {
|
||||
return Beats(_beats - b._beats, _ticks - b._ticks);
|
||||
return ticks (_ticks - b._ticks);
|
||||
}
|
||||
|
||||
Beats operator-() const {
|
||||
/* must avoid normalization here, which will convert a negative
|
||||
value into a valid beat position before zero, which is not
|
||||
we want here.
|
||||
*/
|
||||
Beats b (_beats, _ticks);
|
||||
b._beats = -b._beats;
|
||||
b._ticks = -b._ticks;
|
||||
return b;
|
||||
return ticks (-_ticks);
|
||||
}
|
||||
|
||||
Beats operator*(int32_t factor) const {return ticks (to_ticks() * factor); }
|
||||
@ -261,7 +212,6 @@ public:
|
||||
|
||||
Beats operator%= (Beats const & b) {
|
||||
const Beats B (Beats::ticks (to_ticks() % b.to_ticks()));
|
||||
_beats = B._beats;
|
||||
_ticks = B._ticks;
|
||||
return *this;
|
||||
}
|
||||
@ -275,27 +225,22 @@ public:
|
||||
}
|
||||
|
||||
Beats& operator+=(const Beats& b) {
|
||||
_beats += b._beats;
|
||||
_ticks += b._ticks;
|
||||
normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Beats& operator-=(const Beats& b) {
|
||||
_beats -= b._beats;
|
||||
_ticks -= b._ticks;
|
||||
normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!() const { return _beats == 0 && _ticks == 0; }
|
||||
explicit operator bool () const { return _beats != 0 || _ticks != 0; }
|
||||
bool operator!() const { return _ticks == 0; }
|
||||
explicit operator bool () const { return _ticks != 0; }
|
||||
|
||||
static Beats one_tick() { return Beats(0, 1); }
|
||||
|
||||
protected:
|
||||
int32_t _beats;
|
||||
int32_t _ticks;
|
||||
int64_t _ticks;
|
||||
|
||||
};
|
||||
|
||||
@ -307,7 +252,7 @@ class DoubleableBeats : public Beats
|
||||
{
|
||||
public:
|
||||
DoubleableBeats (Beats const & b) : Beats (b) {}
|
||||
double to_double() const { return (double)_beats + (_ticks / (double)PPQN); }
|
||||
double to_double() const { return (double)get_beats() + (get_ticks() / (double)PPQN); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,21 +14,21 @@ void
|
||||
BeatsTest::createTest()
|
||||
{
|
||||
const Beats a(1, 2);
|
||||
CPPUNIT_ASSERT_EQUAL(1, a.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(1), a.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(2, a.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1 + 2 / (double)Beats::PPQN, DoubleableBeats(a).to_double(), delta);
|
||||
|
||||
const Beats b = Beats::from_double (1.5);
|
||||
CPPUNIT_ASSERT_EQUAL(1, b.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(1), b.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(Beats::PPQN / 2, b.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.5, DoubleableBeats (b).to_double(), delta);
|
||||
|
||||
const Beats c = Beats::beats(6);
|
||||
CPPUNIT_ASSERT_EQUAL(6, c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(6), c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(0, c.get_ticks());
|
||||
|
||||
const Beats d = Beats::ticks(7);
|
||||
CPPUNIT_ASSERT_EQUAL(0, d.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), d.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(7, d.get_ticks());
|
||||
|
||||
Beats e(8, 9);
|
||||
@ -50,7 +50,7 @@ BeatsTest::addTest()
|
||||
|
||||
// Positive + positive
|
||||
const Beats c = a + b;
|
||||
CPPUNIT_ASSERT_EQUAL(4, c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(4), c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(6, c.get_ticks());
|
||||
|
||||
const Beats n1 = Beats::from_double (-12.34);
|
||||
@ -62,19 +62,19 @@ BeatsTest::addTest()
|
||||
// Positive + negative
|
||||
const Beats p1 = Beats::from_double (1.0);
|
||||
const Beats p_n = p1 + n1;
|
||||
CPPUNIT_ASSERT_EQUAL(-11, p_n.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(-int64_t(11), p_n.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL((int32_t) rint (Beats::PPQN * -0.34), p_n.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-11.34, DoubleableBeats (p_n).to_double(), delta);
|
||||
|
||||
// Negative + positive
|
||||
const Beats n_p = n1 + p1;
|
||||
CPPUNIT_ASSERT_EQUAL(-11, n_p.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(-int64_t(11), n_p.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL((int32_t) rint (Beats::PPQN * -0.34), n_p.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-11.34, DoubleableBeats (n_p).to_double(), delta);
|
||||
|
||||
// Negative + negative
|
||||
const Beats sum = n1 + n2;
|
||||
CPPUNIT_ASSERT_EQUAL(-69, sum.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(-int64_t(69), sum.get_beats());
|
||||
//CPPUNIT_ASSERT_EQUAL((int32_t)(Beats::PPQN * -0.12), n_p.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-69.12, DoubleableBeats (sum).to_double(), delta);
|
||||
}
|
||||
@ -87,7 +87,7 @@ BeatsTest::subtractTest()
|
||||
|
||||
// Positive - positive
|
||||
const Beats c = b - a;
|
||||
CPPUNIT_ASSERT_EQUAL(2, c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(2), c.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(2, c.get_ticks());
|
||||
|
||||
const Beats n1 = Beats::from_double (-12.34);
|
||||
@ -99,19 +99,19 @@ BeatsTest::subtractTest()
|
||||
// Positive - negative
|
||||
const Beats p1= Beats::from_double (1.0);
|
||||
const Beats p_n = p1 - n1;
|
||||
CPPUNIT_ASSERT_EQUAL(13, p_n.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(13), p_n.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL((int32_t) rint (Beats::PPQN * 0.34), p_n.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(13.34, DoubleableBeats (p_n).to_double(), delta);
|
||||
|
||||
// Negative - positive
|
||||
const Beats n_p = n1 - p1;
|
||||
CPPUNIT_ASSERT_EQUAL(-13, n_p.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(-int64_t(13), n_p.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL((int32_t) rint (Beats::PPQN * -0.34), n_p.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-13.34, DoubleableBeats (n_p).to_double(), delta);
|
||||
|
||||
// Negative - negative
|
||||
const Beats diff = n1 - n2;
|
||||
CPPUNIT_ASSERT_EQUAL(44, diff.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(44), diff.get_beats());
|
||||
CPPUNIT_ASSERT_EQUAL((int32_t)lrint(Beats::PPQN * 0.44), diff.get_ticks());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(44.44, DoubleableBeats (diff).to_double(), delta);
|
||||
}
|
||||
@ -131,12 +131,12 @@ BeatsTest::roundTest()
|
||||
|
||||
// Round a up
|
||||
const Beats au = a.round_up_to_beat();
|
||||
CPPUNIT_ASSERT_EQUAL(au.get_beats(), 2);
|
||||
CPPUNIT_ASSERT_EQUAL(au.get_beats(), int64_t(2));
|
||||
CPPUNIT_ASSERT_EQUAL(au.get_ticks(), 0);
|
||||
|
||||
// Round a down
|
||||
const Beats ad = a.round_down_to_beat();
|
||||
CPPUNIT_ASSERT_EQUAL(ad.get_beats(), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(ad.get_beats(), int64_t (1));
|
||||
CPPUNIT_ASSERT_EQUAL(ad.get_ticks(), 0);
|
||||
|
||||
// Round result down again
|
||||
@ -149,7 +149,7 @@ BeatsTest::roundTest()
|
||||
|
||||
// Snap to 1.5
|
||||
const Beats snapped = a.round_to_multiple (Beats::from_double (1.5));
|
||||
CPPUNIT_ASSERT_EQUAL(snapped.get_beats(), 1);
|
||||
CPPUNIT_ASSERT_EQUAL(snapped.get_beats(), int64_t (1));
|
||||
CPPUNIT_ASSERT_EQUAL(snapped.get_ticks(), Beats::PPQN / 2);
|
||||
}
|
||||
|
||||
@ -157,12 +157,12 @@ void
|
||||
BeatsTest::convertTest()
|
||||
{
|
||||
const Beats a = Beats::ticks_at_rate(72000, 48000);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1, a.get_beats(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(int64_t (1), a.get_beats(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(Beats::PPQN / 2, a.get_ticks(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.5, DoubleableBeats(a).to_double(), delta);
|
||||
|
||||
const Beats b = Beats::ticks_at_rate(8, 48000);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0, b.get_beats(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(int64_t (0), b.get_beats(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(Beats::PPQN * 8 / 48000, b.get_ticks(), delta);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL((8 / 48000.0), DoubleableBeats (b).to_double(), delta);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user