extend/fix/improve operator overloads and methods for Timecode::BBT_Time
[ardour.git] / libs / timecode / timecode / bbt_time.h
1 /*
2   Copyright (C) 2002-2010 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or (at your
7   option) any later version.
8
9   This program is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12   License for more details.
13
14   You should have received a copy of the GNU Lesser General Public License
15   along with this program; if not, write to the Free Software Foundation,
16   Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #ifndef __timecode_bbt_time_h__
20 #define __timecode_bbt_time_h__
21
22 #include <ostream>
23 #include <stdint.h>
24 #include <iomanip>
25 #include <exception>
26
27 #include "timecode/visibility.h"
28
29 namespace Timecode {
30
31 /** Bar, Beat, Tick Time (i.e. Tempo-Based Time) */
32 struct LIBTIMECODE_API BBT_Time
33 {
34         static const double ticks_per_beat;
35
36         /* note that it is illegal for BBT_Time to have bars==0 or
37          * beats==0. The "neutral" or "default" value is 1|1|0
38          */
39
40         int32_t bars;
41         int32_t beats;
42         int32_t ticks;
43
44         struct IllegalBBTTimeException : public std::exception {
45                 virtual const char* what() const throw() { return "illegal BBT time (bars or beats were zero)"; }
46         };
47
48         BBT_Time () : bars (1), beats (1), ticks (0) {}
49         BBT_Time (int32_t ba, uint32_t be, uint32_t t) : bars (ba), beats (be), ticks (t) { if (!bars || !beats) { throw IllegalBBTTimeException(); } }
50
51         bool operator< (const BBT_Time& other) const {
52                 return bars < other.bars ||
53                         (bars == other.bars && beats < other.beats) ||
54                         (bars == other.bars && beats == other.beats && ticks < other.ticks);
55         }
56
57         bool operator<= (const BBT_Time& other) const {
58                 return bars < other.bars ||
59                         (bars <= other.bars && beats <= other.beats) ||
60                         (bars <= other.bars && beats <= other.beats && ticks <= other.ticks);
61         }
62
63         bool operator> (const BBT_Time& other) const {
64                 return bars > other.bars ||
65                         (bars == other.bars && beats > other.beats) ||
66                         (bars == other.bars && beats == other.beats && ticks > other.ticks);
67         }
68
69         bool operator>= (const BBT_Time& other) const {
70                 return bars > other.bars ||
71                         (bars >= other.bars && beats >= other.beats) ||
72                         (bars >= other.bars && beats >= other.beats && ticks >= other.ticks);
73         }
74
75         bool operator== (const BBT_Time& other) const {
76                 return bars == other.bars && beats == other.beats && ticks == other.ticks;
77         }
78
79         bool operator!= (const BBT_Time& other) const {
80                 return bars != other.bars || beats != other.beats || ticks != other.ticks;
81         }
82
83         /* it would be nice to provide operator+(BBT_Time const&) and
84          * operator-(BBT_Time const&) but this math requires knowledge of the
85          * meter (time signature) used to define 1 bar, and so cannot be
86          * carried out with only two BBT_Time values.
87          */
88
89         BBT_Time round_to_beat () const { return ticks >= (ticks_per_beat/2) ? BBT_Time (bars, beats+1, 0) : BBT_Time (bars, beats, 0); }
90         BBT_Time round_down_to_beat () const { return BBT_Time (bars, beats, 0); }
91         BBT_Time round_up_to_beat () const { return ticks ? BBT_Time (bars, beats+1, 0) : *this; }
92
93         /* cannot implement round_to_bar() without knowing meter (time
94          * signature) information.
95          */
96 };
97
98 struct LIBTIMECODE_API BBT_Offset
99 {
100         int32_t bars;
101         int32_t beats;
102         int32_t ticks;
103
104         /* this is a variant for which bars==0 and/or beats==0 is legal. It
105          * represents an offset from a given BBT_Time and is used when doing
106          * add/subtract operations on a BBT_Time.
107          */
108
109         BBT_Offset () : bars (0), beats (0), ticks (0) {}
110         BBT_Offset (int32_t ba, uint32_t be, uint32_t t) : bars (ba), beats (be), ticks (t) {}
111         BBT_Offset (BBT_Time const & bbt) : bars (bbt.bars), beats (bbt.beats), ticks (bbt.ticks) {}
112         BBT_Offset (double beats);
113 };
114
115 }
116
117 inline std::ostream&
118 operator<< (std::ostream& o, const Timecode::BBT_Time& bbt)
119 {
120         o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
121         return o;
122 }
123
124 inline std::ostream&
125 operator<< (std::ostream& o, const Timecode::BBT_Offset& bbt)
126 {
127         o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
128         return o;
129 }
130
131 inline std::ostream&
132 print_padded (std::ostream& o, const Timecode::BBT_Time& bbt)
133 {
134         o << std::setfill ('0') << std::right
135           << std::setw (3) << bbt.bars << "|"
136           << std::setw (2) << bbt.beats << "|"
137           << std::setw (4) << bbt.ticks;
138
139         return o;
140 }
141
142 #endif /* __timecode_bbt_time_h__ */