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