OXYGENHUB
◆ main openmsx / src / EmuDuration.hh
5.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef EMUDURATION_HH
2
#define EMUDURATION_HH
3
4
#include "narrow.hh"
5
#include "serialize.hh"
6
7
#include <cassert>
8
#include <concepts>
9
#include <cstdint>
10
#include <limits>
11
#include <type_traits>
12
13
namespace openmsx {
14
15
// constants
16
inline constexpr uint64_t MAIN_FREQ = 3579545ULL * 960;
17
inline constexpr unsigned MAIN_FREQ32 = MAIN_FREQ;
18
static_assert(MAIN_FREQ < (1ULL << 32), "must fit in 32 bit");
19
inline constexpr double RECIP_MAIN_FREQ = 1.0 / MAIN_FREQ;
20
21
22
class EmuDuration
23
{
24
public:
25
	// friends
26
	friend class EmuTime;
27
28
	// constructors
29
	constexpr EmuDuration() = default;
30
	constexpr explicit EmuDuration(std::unsigned_integral auto n) : time(n) {}
31
32
	static constexpr EmuDuration sec(std::integral auto x)
33
		{ return EmuDuration(uint64_t(x * MAIN_FREQ)); }
34
	static constexpr EmuDuration sec(std::floating_point auto x)
35
		{ return EmuDuration(uint64_t(x * MAIN_FREQ + 0.5)); }
36
	static constexpr EmuDuration msec(std::integral auto x)
37
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1000)); }
38
	static constexpr EmuDuration msec(std::floating_point auto x)
39
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1e3 + 0.5)); }
40
	static constexpr EmuDuration usec(std::integral auto x)
41
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1000000)); }
42
	static constexpr EmuDuration usec(std::floating_point auto x)
43
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1e6 + 0.5)); }
44
	static constexpr EmuDuration nsec(std::integral auto x)
45
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1000000000)); }
46
	static constexpr EmuDuration nsec(std::floating_point auto x)
47
		{ return EmuDuration(uint64_t(x * MAIN_FREQ / 1e9 + 0.5)); }
48
	static constexpr EmuDuration hz(std::integral auto x)
49
		{ return EmuDuration(uint64_t(MAIN_FREQ / x)); }
50
	static constexpr EmuDuration hz(std::floating_point auto x)
51
		{ return EmuDuration(uint64_t(MAIN_FREQ / x + 0.5)); }
52
53
	// conversions
54
	[[nodiscard]] constexpr double toDouble() const { return double(time) * RECIP_MAIN_FREQ; }
55
	[[nodiscard]] constexpr uint64_t toUint64() const { return time; }
56
57
	// comparison operators
58
	[[nodiscard]] constexpr auto operator<=>(const EmuDuration&) const = default;
59
60
	// arithmetic operators
61
	[[nodiscard]] constexpr friend EmuDuration operator%(EmuDuration l, EmuDuration r)
62
		{ return EmuDuration(l.time % r.time); }
63
	[[nodiscard]] constexpr friend EmuDuration operator+(EmuDuration l, EmuDuration r)
64
		{ return EmuDuration(l.time + r.time); }
65
	[[nodiscard]] constexpr friend EmuDuration operator-(EmuDuration l, EmuDuration r)
66
		{ assert(l.time >= r.time); return EmuDuration(l.time - r.time); }
67
	[[nodiscard]] constexpr friend EmuDuration operator*(EmuDuration l, std::integral auto fact)
68
		{ return EmuDuration(l.time * fact); }
69
	[[nodiscard]] constexpr friend EmuDuration operator*(EmuDuration l, double fact)
70
		{ return EmuDuration(narrow_cast<uint64_t>(narrow_cast<double>(l.time) * fact)); }
71
	[[nodiscard]] constexpr friend EmuDuration operator/(EmuDuration l, unsigned fact)
72
		{ return EmuDuration(l.time / fact); }
73
	[[nodiscard]] constexpr EmuDuration divRoundUp(unsigned fact) const
74
		{ return EmuDuration((time + fact - 1) / fact); }
75
	[[nodiscard]] constexpr friend unsigned operator/(EmuDuration l, EmuDuration r)
76
	{
77
		uint64_t result = l.time / r.time;
78
#ifdef DEBUG
79
		// we don't even want this overhead in devel builds
80
		assert(result == unsigned(result));
81
#endif
82
		return unsigned(result);
83
	}
84
	[[nodiscard]] constexpr unsigned divUp(EmuDuration d) const {
85
		uint64_t result = (time + d.time - 1) / d.time;
86
#ifdef DEBUG
87
		assert(result == unsigned(result));
88
#endif
89
		return unsigned(result);
90
	}
91
	[[nodiscard]] constexpr double div(EmuDuration d) const
92
		{ return narrow_cast<double>(time) / narrow_cast<double>(d.time); }
93
94
	constexpr EmuDuration& operator*=(unsigned fact)
95
		{ time *= fact; return *this; }
96
	constexpr EmuDuration& operator*=(double fact)
97
		{ time = narrow_cast<uint64_t>(narrow_cast<double>(time) * fact); return *this; }
98
	constexpr EmuDuration& operator/=(double fact)
99
		{ time = narrow_cast<uint64_t>(narrow_cast<double>(time) / fact); return *this; }
100
101
	// The smallest duration larger than zero
102
	[[nodiscard]] static constexpr EmuDuration epsilon() {
103
		return EmuDuration(uint64_t(1));
104
	}
105
106
	// ticks
107
	// TODO: Used in WavAudioInput. Keep or use DynamicClock instead?
108
	[[nodiscard]] constexpr unsigned getTicksAt(unsigned freq) const
109
	{
110
		uint64_t result = time / (MAIN_FREQ32 / freq);
111
#ifdef DEBUG
112
		// we don't even want this overhead in devel builds
113
		assert(result == unsigned(result));
114
#endif
115
		return unsigned(result);
116
	}
117
118
	[[nodiscard]] static constexpr EmuDuration zero()
119
	{
120
		return EmuDuration(uint64_t(0));
121
	}
122
123
	[[nodiscard]] static constexpr EmuDuration infinity()
124
	{
125
		return EmuDuration(std::numeric_limits<uint64_t>::max());
126
	}
127
128
	template<typename Archive>
129
	void serialize(Archive& ar, unsigned /*version*/)
130
	{
131
		ar.serialize("time", time);
132
	}
133
134
private:
135
	uint64_t time = 0;
136
};
137
138
template<> struct SerializeAsMemcpy<EmuDuration> : std::true_type {};
139
140
141
template<std::unsigned_integral T> class EmuDurationCompactStorage
142
{
143
public:
144
	explicit constexpr EmuDurationCompactStorage(EmuDuration e)
145
		: time(T(e.toUint64()))
146
	{
147
		assert(e.toUint64() <= std::numeric_limits<T>::max());
148
	}
149
150
	[[nodiscard]] explicit constexpr operator EmuDuration() const
151
	{
152
		return EmuDuration(uint64_t(time));
153
	}
154
private:
155
	T time;
156
};
157
158
using EmuDuration32 = EmuDurationCompactStorage<uint32_t>;
159
using EmuDuration16 = EmuDurationCompactStorage<uint16_t>;
160
using EmuDuration8  = EmuDurationCompactStorage<uint8_t>;
161
162
template<uint64_t MAX>
163
using EmuDurationStorageFor = std::conditional_t<(MAX > std::numeric_limits<uint32_t>::max()), EmuDuration,
164
                              std::conditional_t<(MAX > std::numeric_limits<uint16_t>::max()), EmuDuration32,
165
                              std::conditional_t<(MAX > std::numeric_limits<uint8_t >::max()), EmuDuration16,
166
                                                                                               EmuDuration8>>>;
167
} // namespace openmsx
168
169
#endif