summaryrefslogtreecommitdiff
path: root/libs/evoral/evoral/types.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/evoral/evoral/types.hpp')
-rw-r--r--libs/evoral/evoral/types.hpp205
1 files changed, 172 insertions, 33 deletions
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