1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "audio/timestamp.h"
24 #include "common/algorithm.h"
25
26 namespace Audio {
27
Timestamp(uint ms,uint fr)28 Timestamp::Timestamp(uint ms, uint fr) {
29 assert(fr > 0);
30
31 _secs = ms / 1000;
32 _framerateFactor = 1000 / Common::gcd<uint>(1000, fr);
33 _framerate = fr * _framerateFactor;
34
35 // Note that _framerate is always divisible by 1000.
36 _numFrames = (ms % 1000) * (_framerate / 1000);
37 }
38
Timestamp(uint s,uint frames,uint fr)39 Timestamp::Timestamp(uint s, uint frames, uint fr) {
40 assert(fr > 0);
41
42 _secs = s + (frames / fr);
43 _framerateFactor = 1000 / Common::gcd<uint>(1000, fr);
44 _framerate = fr * _framerateFactor;
45 _numFrames = (frames % fr) * _framerateFactor;
46 }
47
convertToFramerate(uint newFramerate) const48 Timestamp Timestamp::convertToFramerate(uint newFramerate) const {
49 Timestamp ts(*this);
50
51 if (ts.framerate() != newFramerate) {
52 ts._framerateFactor = 1000 / Common::gcd<uint>(1000, newFramerate);
53 ts._framerate = newFramerate * ts._framerateFactor;
54
55 const uint g = Common::gcd(_framerate, ts._framerate);
56 const uint p = _framerate / g;
57 const uint q = ts._framerate / g;
58
59 // Convert the frame offset to the new framerate.
60 // We round to the nearest (as opposed to always
61 // rounding down), to minimize rounding errors during
62 // round trip conversions.
63 ts._numFrames = (ts._numFrames * q + p/2) / p;
64
65 ts.normalize();
66 }
67
68 return ts;
69 }
70
normalize()71 void Timestamp::normalize() {
72 // Convert negative _numFrames values to positive ones by adjusting _secs
73 if (_numFrames < 0) {
74 int secsub = 1 + (-_numFrames / _framerate);
75
76 _numFrames += _framerate * secsub;
77 _secs -= secsub;
78 }
79
80 // Wrap around if necessary
81 _secs += (_numFrames / _framerate);
82 _numFrames %= _framerate;
83 }
84
operator ==(const Timestamp & ts) const85 bool Timestamp::operator==(const Timestamp &ts) const {
86 return cmp(ts) == 0;
87 }
88
operator !=(const Timestamp & ts) const89 bool Timestamp::operator!=(const Timestamp &ts) const {
90 return cmp(ts) != 0;
91 }
92
operator <(const Timestamp & ts) const93 bool Timestamp::operator<(const Timestamp &ts) const {
94 return cmp(ts) < 0;
95 }
96
operator <=(const Timestamp & ts) const97 bool Timestamp::operator<=(const Timestamp &ts) const {
98 return cmp(ts) <= 0;
99 }
100
operator >(const Timestamp & ts) const101 bool Timestamp::operator>(const Timestamp &ts) const {
102 return cmp(ts) > 0;
103 }
104
operator >=(const Timestamp & ts) const105 bool Timestamp::operator>=(const Timestamp &ts) const {
106 return cmp(ts) >= 0;
107 }
108
cmp(const Timestamp & ts) const109 int Timestamp::cmp(const Timestamp &ts) const {
110 int delta = _secs - ts._secs;
111 if (!delta) {
112 const uint g = Common::gcd(_framerate, ts._framerate);
113 const uint p = _framerate / g;
114 const uint q = ts._framerate / g;
115
116 delta = (_numFrames * q - ts._numFrames * p);
117 }
118
119 return delta;
120 }
121
122
addFrames(int frames) const123 Timestamp Timestamp::addFrames(int frames) const {
124 Timestamp ts(*this);
125
126 // The frames are given in the original framerate, so we have to
127 // adjust by _framerateFactor accordingly.
128 ts._numFrames += frames * _framerateFactor;
129 ts.normalize();
130
131 return ts;
132 }
133
addMsecs(int ms) const134 Timestamp Timestamp::addMsecs(int ms) const {
135 Timestamp ts(*this);
136 ts._secs += ms / 1000;
137 // Add the remaining frames. Note that _framerate is always divisible by 1000.
138 ts._numFrames += (ms % 1000) * (ts._framerate / 1000);
139
140 ts.normalize();
141
142 return ts;
143 }
144
addIntern(const Timestamp & ts)145 void Timestamp::addIntern(const Timestamp &ts) {
146 assert(_framerate == ts._framerate);
147 _secs += ts._secs;
148 _numFrames += ts._numFrames;
149
150 normalize();
151 }
152
operator -() const153 Timestamp Timestamp::operator-() const {
154 Timestamp result(*this);
155 result._secs = -_secs;
156 result._numFrames = -_numFrames;
157 result.normalize();
158 return result;
159 }
160
operator +(const Timestamp & ts) const161 Timestamp Timestamp::operator+(const Timestamp &ts) const {
162 Timestamp result(*this);
163 result.addIntern(ts);
164 return result;
165 }
166
operator -(const Timestamp & ts) const167 Timestamp Timestamp::operator-(const Timestamp &ts) const {
168 Timestamp result(*this);
169 result.addIntern(-ts);
170 return result;
171 }
172
frameDiff(const Timestamp & ts) const173 int Timestamp::frameDiff(const Timestamp &ts) const {
174
175 int delta = 0;
176 if (_secs != ts._secs)
177 delta = (_secs - ts._secs) * _framerate;
178
179 delta += _numFrames;
180
181 if (_framerate == ts._framerate) {
182 delta -= ts._numFrames;
183 } else {
184 // We need to multiply by the quotient of the two framerates.
185 // We cancel the GCD in this fraction to reduce the risk of
186 // overflows.
187 const uint g = Common::gcd(_framerate, ts._framerate);
188 const uint p = _framerate / g;
189 const uint q = ts._framerate / g;
190
191 delta -= ((long)ts._numFrames * p + q/2) / (long)q;
192 }
193
194 return delta / (int)_framerateFactor;
195 }
196
msecsDiff(const Timestamp & ts) const197 int Timestamp::msecsDiff(const Timestamp &ts) const {
198 return msecs() - ts.msecs();
199 }
200
msecs() const201 int Timestamp::msecs() const {
202 // Note that _framerate is always divisible by 1000.
203 return _secs * 1000 + _numFrames / (_framerate / 1000);
204 }
205
206
207 } // End of namespace Audio
208