diff options
author | David Robillard <d@drobilla.net> | 2014-11-22 04:05:42 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-11-22 04:05:42 -0500 |
commit | c1cfa12d6e5136d2e3e5501e83ff74c5009a9e60 (patch) | |
tree | 56d2811bc8b9d6f2a5accfa8e497ddd5976c7c7a /libs/evoral/evoral | |
parent | cae74309a583c29dd6cc2081425c2e7b673ea13e (diff) |
Wrap MusicalTime in a class.
This lets us get a more explicit handle on time conversions, and is the main
step towards using actual beat:tick time and getting away from floating point
precision problems.
Diffstat (limited to 'libs/evoral/evoral')
-rw-r--r-- | libs/evoral/evoral/Event.hpp | 16 | ||||
-rw-r--r-- | libs/evoral/evoral/Note.hpp | 10 | ||||
-rw-r--r-- | libs/evoral/evoral/PatchChange.hpp | 4 | ||||
-rw-r--r-- | libs/evoral/evoral/Sequence.hpp | 14 | ||||
-rw-r--r-- | libs/evoral/evoral/types.hpp | 205 |
5 files changed, 193 insertions, 56 deletions
diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp index 59e5612e0e..059b64225b 100644 --- a/libs/evoral/evoral/Event.hpp +++ b/libs/evoral/evoral/Event.hpp @@ -48,7 +48,7 @@ template<typename Time> class LIBEVORAL_API Event { public: #ifdef EVORAL_EVENT_ALLOC - Event (EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false); + Event (EventType type=0, Time time=Time(), uint32_t size=0, uint8_t* buf=NULL, bool alloc=false); /** Copy \a copy. * @@ -114,11 +114,11 @@ public: } inline void clear() { - _type = 0; - _original_time = 0; - _nominal_time = 0; - _size = 0; - _buf = NULL; + _type = 0; + _original_time = Time(); + _nominal_time = Time(); + _size = 0; + _buf = NULL; } #else @@ -154,9 +154,6 @@ protected: #endif }; -} // namespace Evoral - - template<typename Time> /*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream& o, const Evoral::Event<Time>& ev) { o << "Event #" << ev.id() << " type = " << ev.event_type() << " @ " << ev.time(); @@ -168,6 +165,7 @@ template<typename Time> return o; } +} // namespace Evoral #endif // EVORAL_EVENT_HPP diff --git a/libs/evoral/evoral/Note.hpp b/libs/evoral/evoral/Note.hpp index 5401271621..87c8a9fe83 100644 --- a/libs/evoral/evoral/Note.hpp +++ b/libs/evoral/evoral/Note.hpp @@ -39,16 +39,16 @@ class LIBEVORAL_LOCAL Note { class LIBEVORAL_TEMPLATE_API Note { #endif public: - Note(uint8_t chan=0, Time time=0, Time len=0, uint8_t note=0, uint8_t vel=0x40); + Note(uint8_t chan=0, Time time=Time(), Time len=Time(), uint8_t note=0, uint8_t vel=0x40); Note(const Note<Time>& copy); ~Note(); const Note<Time>& operator=(const Note<Time>& copy); inline bool operator==(const Note<Time>& other) { - return musical_time_equal (time(), other.time()) && + return time() == other.time() && note() == other.note() && - musical_time_equal (length(), other.length()) && + length() == other.length() && velocity() == other.velocity() && off_velocity() == other.off_velocity() && channel() == other.channel(); @@ -109,8 +109,6 @@ private: MIDIEvent<Time> _off_event; }; -} // namespace Evoral - template<typename Time> /*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream& o, const Evoral::Note<Time>& n) { o << "Note #" << n.id() << ": pitch = " << (int) n.note() @@ -120,6 +118,8 @@ template<typename Time> return o; } +} // namespace Evoral + #ifdef COMPILER_MSVC #include "../src/Note.impl" #endif diff --git a/libs/evoral/evoral/PatchChange.hpp b/libs/evoral/evoral/PatchChange.hpp index 48ed0f9c13..39ea421242 100644 --- a/libs/evoral/evoral/PatchChange.hpp +++ b/libs/evoral/evoral/PatchChange.hpp @@ -120,7 +120,7 @@ public: uint8_t channel () const { return _program_change.buffer()[0] & 0xf; } inline bool operator< (const PatchChange<Time>& o) const { - if (!musical_time_equal (time(), o.time())) { + if (time() != o.time()) { return time() < o.time(); } @@ -132,7 +132,7 @@ public: } inline bool operator== (const PatchChange<Time>& o) const { - return (musical_time_equal (time(), o.time()) && program() == o.program() && bank() == o.bank()); + return (time() == o.time() && program() == o.program() && bank() == o.bank()); } /** The PatchChange is made up of messages() MIDI messages; this method returns them by index. diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index 484afa8459..e40c4da925 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -105,7 +105,7 @@ public: ResolveStuckNotes }; - void end_write (StuckNoteOption, Time when = 0); + void end_write (StuckNoteOption, Time when = Time()); void append(const Event<Time>& ev, Evoral::event_id_t evid); @@ -127,7 +127,7 @@ public: struct EarlierNoteComparator { inline bool operator()(const boost::shared_ptr< const Note<Time> > a, const boost::shared_ptr< const Note<Time> > b) const { - return musical_time_less_than (a->time(), b->time()); + return a->time() < b->time(); } }; @@ -135,7 +135,7 @@ public: typedef const Note<Time>* value_type; inline bool operator()(const boost::shared_ptr< const Note<Time> > a, const boost::shared_ptr< const Note<Time> > b) const { - return musical_time_greater_than (a->time(), b->time()); + return a->time() > b->time(); } }; @@ -143,7 +143,7 @@ public: typedef const Note<Time>* value_type; inline bool operator()(const boost::shared_ptr< const Note<Time> > a, const boost::shared_ptr< const Note<Time> > b) const { - return musical_time_greater_than (a->end_time(), b->end_time()); + return a->end_time() > b->end_time(); } }; @@ -187,7 +187,7 @@ public: struct EarlierSysExComparator { inline bool operator() (constSysExPtr a, constSysExPtr b) const { - return musical_time_less_than (a->time(), b->time()); + return a->time() < b->time(); } }; @@ -200,7 +200,7 @@ public: struct EarlierPatchChangeComparator { inline bool operator() (constPatchChangePtr a, constPatchChangePtr b) const { - return musical_time_less_than (a->time(), b->time()); + return a->time() < b->time(); } }; @@ -262,7 +262,7 @@ public: }; const_iterator begin ( - Time t = 0, + Time t = Time(), bool force_discrete = false, std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const { return const_iterator (*this, t, force_discrete, f); diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp index b642fba4c2..d3913f0979 100644 --- a/libs/evoral/evoral/types.hpp +++ b/libs/evoral/evoral/types.hpp @@ -19,15 +19,18 @@ #ifndef EVORAL_TYPES_HPP #define EVORAL_TYPES_HPP +#include <float.h> +#include <math.h> #include <stdint.h> -#include <list> -#include <cmath> -#include <cfloat> -#include "pbd/debug.h" +#include <iostream> +#include <limits> +#include <list> #include "evoral/visibility.h" +#include "pbd/debug.h" + namespace Evoral { /** ID of an event (note or other). This must be operable on by glib @@ -36,47 +39,174 @@ namespace Evoral { typedef int32_t event_id_t; /** Musical time: beats relative to some defined origin */ -typedef double MusicalTime; +class LIBEVORAL_API MusicalTime { +public: + MusicalTime() : _time(0.0) {} -const MusicalTime MaxMusicalTime = DBL_MAX; -const MusicalTime MinMusicalTime = DBL_MIN; + /** Create from a real number of beats. */ + explicit MusicalTime(double time) : _time(time) {} -static inline bool musical_time_equal (MusicalTime a, MusicalTime b) { - /* acceptable tolerance is 1 tick. Nice if there was no magic number here - * -> Timecode::BBT_Time::ticks_per_beat */ - return fabs (a - b) <= (1.0/1920.0); -} + /** Create from an integer number of beats. */ + static MusicalTime beats(int32_t beats) { + return MusicalTime((double)beats); + } -static inline bool musical_time_less_than (MusicalTime a, MusicalTime b) { - /* acceptable tolerance is 1 tick. Nice if there was no magic number here */ - if (fabs (a - b) <= (1.0/1920.0)) { - return false; /* effectively identical */ - } else { - return a < b; + /** Create from ticks at the standard PPQN. */ + static MusicalTime ticks(uint32_t ticks) { + return MusicalTime(ticks / _ppqn); } -} -static inline bool musical_time_greater_than (MusicalTime a, MusicalTime b) { - /* acceptable tolerance is 1 tick. Nice if there was no magic number here */ - if (fabs (a - b) <= (1.0/1920.0)) { - return false; /* effectively identical */ - } else { - return a > b; + /** Create from ticks at a given rate. + * + * Note this can also be used to create from frames by setting ppqn to the + * number of samples per beat. + */ + static MusicalTime ticks_at_rate(uint64_t ticks, uint32_t ppqn) { + return MusicalTime((double)ticks / (double)ppqn); } -} -static inline bool musical_time_greater_or_equal_to (MusicalTime a, MusicalTime b) { - /* acceptable tolerance is 1 tick. Nice if there was no magic number here */ - if (fabs (a - b) <= (1.0/1920.0)) { - return true; /* effectively identical, note the "or_equal_to" */ - } else { - return a >= b; + MusicalTime& operator=(const MusicalTime& other) { + _time = other._time; + return *this; + } + + MusicalTime round_up_to_beat() const { + return Evoral::MusicalTime(ceil(_time)); + } + + MusicalTime round_down_to_beat() const { + return Evoral::MusicalTime(floor(_time)); + } + + MusicalTime snap_to(const Evoral::MusicalTime& snap) const { + return MusicalTime(ceil(_time / snap._time) * snap._time); + } + + inline bool operator==(const MusicalTime& b) const { + /* Acceptable tolerance is 1 tick. */ + return fabs(_time - b._time) <= (1.0/_ppqn); + } + + inline bool operator==(double t) const { + /* Acceptable tolerance is 1 tick. */ + return fabs(_time - t) <= (1.0/_ppqn); + } + + inline bool operator==(int beats) const { + /* Acceptable tolerance is 1 tick. */ + return fabs(_time - beats) <= (1.0/_ppqn); + } + + inline bool operator!=(const MusicalTime& b) const { + return !operator==(b); + } + + inline bool operator<(const MusicalTime& b) const { + /* Acceptable tolerance is 1 tick. */ + if (fabs(_time - b._time) <= (1.0/_ppqn)) { + return false; /* Effectively identical. */ + } else { + return _time < b._time; + } + } + + inline bool operator<=(const MusicalTime& b) const { + return operator==(b) || operator<(b); + } + + inline bool operator>(const MusicalTime& b) const { + /* Acceptable tolerance is 1 tick. */ + if (fabs(_time - b._time) <= (1.0/_ppqn)) { + return false; /* Effectively identical. */ + } else { + return _time > b._time; + } } -} + + inline bool operator>=(const MusicalTime& b) const { + /* Acceptable tolerance is 1 tick. */ + if (fabs(_time - b._time) <= (1.0/_ppqn)) { + return true; /* Effectively identical. */ + } else { + return _time >= b._time; + } + } + + MusicalTime operator+(const MusicalTime& b) const { + return MusicalTime(_time + b._time); + } + + MusicalTime operator-(const MusicalTime& b) const { + return MusicalTime(_time - b._time); + } + + MusicalTime operator-() const { + return MusicalTime(-_time); + } + + template<typename Number> + MusicalTime operator*(Number factor) const { + return MusicalTime(_time * factor); + } + + MusicalTime& operator+=(const MusicalTime& b) { + _time += b._time; + return *this; + } + + MusicalTime& operator-=(const MusicalTime& b) { + _time -= b._time; + return *this; + } + + double to_double() const { return _time; } + uint64_t to_ticks() const { return lrint(_time * _ppqn); } + uint64_t to_ticks(uint32_t ppqn) const { return lrint(_time * ppqn); } + + operator bool() const { return _time != 0; } + + static MusicalTime min() { return MusicalTime(DBL_MIN); } + static MusicalTime max() { return MusicalTime(DBL_MAX); } + static MusicalTime tick() { return MusicalTime(1.0 / _ppqn); } + +private: + static const double _ppqn = 1920.0; /* TODO: Make configurable. */ + + double _time; +}; + +const MusicalTime MaxMusicalTime = Evoral::MusicalTime::max(); +const MusicalTime MinMusicalTime = Evoral::MusicalTime::min(); /** Type of an event (opaque, mapped by application) */ typedef uint32_t EventType; +/* + TIL, several horrible hours later, that sometimes the compiler looks in the + namespace of a type (Evoral::MusicalTime in this case) for an operator, and + does *NOT* look in the global namespace. + + C++ is proof that hell exists and we are living in it. In any case, move + these to the global namespace and PBD::Property's loopy + virtual-method-in-a-template will bite you. +*/ + +inline std::ostream& +operator<<(std::ostream& os, const MusicalTime& t) +{ + os << t.to_double(); + return os; +} + +inline std::istream& +operator>>(std::istream& is, MusicalTime& t) +{ + double beats; + is >> beats; + t = MusicalTime(beats); + return is; +} + } // namespace Evoral namespace PBD { @@ -84,7 +214,16 @@ namespace PBD { LIBEVORAL_API extern uint64_t Sequence; LIBEVORAL_API extern uint64_t Note; LIBEVORAL_API extern uint64_t ControlList; + LIBEVORAL_API extern uint64_t MusicalTime; } } +namespace std { + template<> + struct numeric_limits<Evoral::MusicalTime> { + static Evoral::MusicalTime min() { return Evoral::MusicalTime::min(); } + static Evoral::MusicalTime max() { return Evoral::MusicalTime::max(); } + }; +} + #endif // EVORAL_TYPES_HPP |