1 /* This file is part of Evoral.
2 * Copyright (C) 2008-2015 David Robillard <http://drobilla.net>
3 * Copyright (C) 2000-2008 Paul Davis
5 * Evoral is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option) any later
10 * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #ifndef EVORAL_BEATS_HPP
20 #define EVORAL_BEATS_HPP
30 #include "evoral/visibility.h"
34 /** Musical time in beats. */
35 class /*LIBEVORAL_API*/ Beats {
37 LIBEVORAL_API static const int32_t PPQN = 1920;
39 Beats() : _beats(0), _ticks(0) {}
41 /** Normalize so ticks is within PPQN. */
43 // First, fix negative ticks with positive beats
51 // Work with positive beats and ticks to normalize
52 const int32_t sign = _beats < 0 ? -1 : 1;
53 int32_t beats = abs(_beats);
54 int32_t ticks = abs(_ticks);
56 // Fix ticks greater than 1 beat
57 while (ticks >= PPQN) {
62 // Set fields with appropriate sign
63 _beats = sign * beats;
64 _ticks = sign * ticks;
67 /** Create from a precise BT time. */
68 explicit Beats(int32_t b, int32_t t) : _beats(b), _ticks(t) {
72 /** Create from a real number of beats. */
73 explicit Beats(double time) {
75 const double frac = modf(time, &whole);
81 /** Create from an integer number of beats. */
82 static Beats beats(int32_t beats) {
83 return Beats(beats, 0);
86 /** Create from ticks at the standard PPQN. */
87 static Beats ticks(int32_t ticks) {
88 return Beats(0, ticks);
91 /** Create from ticks at a given rate.
93 * Note this can also be used to create from frames by setting ppqn to the
94 * number of samples per beat. Note the resulting Beats will, like all
95 * others, have the default PPQN, so this is a potentially lossy
98 static Beats ticks_at_rate(int64_t ticks, uint32_t ppqn) {
99 return Beats(ticks / ppqn, (ticks % ppqn) * PPQN / ppqn);
102 Beats& operator=(double time) {
104 const double frac = modf(time, &whole);
107 _ticks = frac * PPQN;
111 Beats& operator=(const Beats& other) {
112 _beats = other._beats;
113 _ticks = other._ticks;
117 Beats round_to_beat() const {
118 return (_ticks >= (PPQN/2)) ? Beats (_beats + 1, 0) : Beats (_beats, 0);
121 Beats round_up_to_beat() const {
122 return (_ticks == 0) ? *this : Beats(_beats + 1, 0);
125 Beats round_down_to_beat() const {
126 return Beats(_beats, 0);
129 Beats snap_to(const Evoral::Beats& snap) const {
130 const double snap_time = snap.to_double();
131 return Beats(ceil(to_double() / snap_time) * snap_time);
134 inline bool operator==(const Beats& b) const {
135 return _beats == b._beats && _ticks == b._ticks;
138 inline bool operator==(double t) const {
139 /* Acceptable tolerance is 1 tick. */
140 return fabs(to_double() - t) <= (1.0 / PPQN);
143 inline bool operator==(int beats) const {
144 return _beats == beats;
147 inline bool operator!=(const Beats& b) const {
148 return !operator==(b);
151 inline bool operator<(const Beats& b) const {
152 return _beats < b._beats || (_beats == b._beats && _ticks < b._ticks);
155 inline bool operator<=(const Beats& b) const {
156 return _beats < b._beats || (_beats == b._beats && _ticks <= b._ticks);
159 inline bool operator>(const Beats& b) const {
160 return _beats > b._beats || (_beats == b._beats && _ticks > b._ticks);
163 inline bool operator>=(const Beats& b) const {
164 return _beats > b._beats || (_beats == b._beats && _ticks >= b._ticks);
167 inline bool operator<(double b) const {
168 /* Acceptable tolerance is 1 tick. */
169 const double time = to_double();
170 if (fabs(time - b) <= (1.0 / PPQN)) {
171 return false; /* Effectively identical. */
177 inline bool operator<=(double b) const {
178 return operator==(b) || operator<(b);
181 inline bool operator>(double b) const {
182 /* Acceptable tolerance is 1 tick. */
183 const double time = to_double();
184 if (fabs(time - b) <= (1.0 / PPQN)) {
185 return false; /* Effectively identical. */
191 inline bool operator>=(double b) const {
192 return operator==(b) || operator>(b);
195 Beats operator+(const Beats& b) const {
196 return Beats(_beats + b._beats, _ticks + b._ticks);
199 Beats operator-(const Beats& b) const {
200 return Beats(_beats - b._beats, _ticks - b._ticks);
203 Beats operator+(double d) const {
204 return Beats(to_double() + d);
207 Beats operator-(double d) const {
208 return Beats(to_double() - d);
211 Beats operator+(int b) const {
212 return Beats (_beats + b, _ticks);
215 Beats operator-(int b) const {
216 return Beats (_beats - b, _ticks);
219 Beats& operator+=(int b) {
224 Beats& operator-=(int b) {
229 Beats operator-() const {
230 return Beats(-_beats, -_ticks);
233 template<typename Number>
234 Beats operator*(Number factor) const {
235 return Beats(_beats * factor, _ticks * factor);
238 template<typename Number>
239 Beats operator/(Number factor) const {
240 return ticks ((_beats * PPQN + _ticks) / factor);
243 Beats& operator+=(const Beats& b) {
250 Beats& operator-=(const Beats& b) {
257 double to_double() const { return (double)_beats + (_ticks / (double)PPQN); }
258 int64_t to_ticks() const { return (int64_t)_beats * PPQN + _ticks; }
259 int64_t to_ticks(uint32_t ppqn) const { return (int64_t)_beats * ppqn + (_ticks * ppqn / PPQN); }
261 int32_t get_beats() const { return _beats; }
262 int32_t get_ticks() const { return _ticks; }
264 bool operator!() const { return _beats == 0 && _ticks == 0; }
266 static Beats tick() { return Beats(0, 1); }
274 TIL, several horrible hours later, that sometimes the compiler looks in the
275 namespace of a type (Evoral::Beats in this case) for an operator, and
276 does *NOT* look in the global namespace.
278 C++ is proof that hell exists and we are living in it. In any case, move
279 these to the global namespace and PBD::Property's loopy
280 virtual-method-in-a-template will bite you.
284 operator<<(std::ostream& os, const Beats& t)
286 os << t.get_beats() << '.' << t.get_ticks();
291 operator>>(std::istream& is, Beats& t)
299 } // namespace Evoral
303 LIBEVORAL_API extern uint64_t Beats;
309 struct numeric_limits<Evoral::Beats> {
310 static Evoral::Beats lowest() {
311 return Evoral::Beats(std::numeric_limits<int32_t>::min(),
312 std::numeric_limits<int32_t>::min());
315 /* We don't define min() since this has different behaviour for integral and floating point types,
316 but Beats is used as both. Better to avoid providing a min at all
317 than a confusing one. */
319 static Evoral::Beats max() {
320 return Evoral::Beats(std::numeric_limits<int32_t>::max(),
321 std::numeric_limits<int32_t>::max());
326 #endif // EVORAL_BEATS_HPP