HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_utils.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // min(), max()
4#include <cctype> // isalpha()
5#include <cmath> // pow()
6#include <exception>
7#include <fstream>
8#include <limits> // infinity(), nan()
9#include <memory>
10#include <ostream>
11#include <string>
12#include <unordered_map>
13
15
16namespace hart
17{
18
19/// @defgroup Utilities Utilities
20/// @brief Handy functions and constants
21/// @{
22
23/// @brief Infinity
24constexpr double inf = std::numeric_limits<double>::infinity();
25
26/// @brief Infinity
27constexpr double oo = inf;
28
29/// @brief pi
30constexpr double pi = 3.14159265358979323846;
31
32/// @brief 2 * pi
33constexpr double twoPi = 2.0 * pi;
34
35/// @brief pi / 2
36constexpr double halfPi = pi / 2.0;
37
38/// @brief Helper values for channel indices
40{
41 left = 0,
42 right = 1
43};
44
45/// @brief Helper values for mid-side channel indices
47{
48 mid = 0,
49 side = 1
50};
51
52/// @brief Helper values for something that could loop, like a Signal
53enum class Loop
54{
55 no,
56 yes
57};
58
59/// @brief Oversampling ratio
61{
62 x4 = 4,
63 x8 = 8,
64 x16 = 16
65};
66
67/// @brief @brief Interpolation method
68enum class Interpolation
69{
70 nearest,
71 linear
72};
73
74inline std::ostream& operator<< (std::ostream& os, Oversampling oversampling)
75{
76 return os << "Oversampling::x" << static_cast<int> (oversampling);
77}
78
79/// @brief Returns a quiet NaN value for the given floating-point type.
80template<typename FloatType>
81inline FloatType nan()
82{
83 return std::numeric_limits<FloatType>::quiet_NaN();
84}
85
86/// @brief `std::clamp()` replacement for C++11
87template <typename NumericType>
88NumericType clamp (const NumericType& value, const NumericType& low, const NumericType& high)
89{
90 return std::min<NumericType> (std::max<NumericType> (value, low), high);
91}
92
93/// @brief Converts dB to linear value (ratio)
94/// @param valueDb Value in decibels
95/// @return Value in linear domain
96template <typename SampleType>
97inline static SampleType decibelsToRatio (SampleType valueDb)
98{
99 if (valueDb < -120)
100 return 0;
101
102 return std::pow (static_cast<SampleType> (10), valueDb / static_cast<SampleType> (20));
103}
104
105/// @brief Converts linear value (ratio) to dB
106/// @param valueLinear Value in linear domain
107/// @return Value in decibels
108template <typename SampleType>
109inline static SampleType ratioToDecibels (SampleType valueLinear)
110{
111 if (valueLinear < 1e-6)
112 return -120;
113
114 return static_cast<SampleType> (20 * std::log10 (valueLinear));
115}
116
117/// @brief Converts dB to linear value (power)
118/// @param valueDb Value in decibels
119/// @return Value in linear domain
120template <typename SampleType>
121inline static SampleType decibelsToPower (SampleType valueDb)
122{
123 if (valueDb < -120)
124 return 0;
125
126 return std::pow (static_cast<SampleType> (10), valueDb / static_cast<SampleType> (10));
127}
128
129/// @brief Converts linear value (power) to dB
130/// @param valueLinear Value in linear domain
131/// @return Value in decibels
132template <typename SampleType>
133inline static SampleType powerToDecibels (SampleType valueLinear)
134{
135 if (valueLinear < 1e-12)
136 return -120;
137
138 return static_cast<SampleType> (10 * std::log10 (valueLinear));
139}
140
141/// @brief Compares two floating point numbers within a given tolerance
142template <typename SampleType>
143inline static SampleType floatsEqual (SampleType a, SampleType b, SampleType epsilon = (SampleType) 1e-8)
144{
145 return std::abs (a - b) < epsilon;
146}
147
148/// @brief Compares two floating point numbers within a given tolerance
149template <typename SampleType>
150inline static SampleType floatsNotEqual (SampleType a, SampleType b, SampleType epsilon = (SampleType) 1e-8)
151{
152 return std::abs (a - b) >= epsilon;
153}
154
155/// @brief Rounds a floating point value to a `size_t` value
156template <typename SampleType>
157inline static size_t roundToSizeT (SampleType x)
158{
159 return static_cast<size_t> (x + (SampleType) 0.5);
160}
161
162/// @brief Anns an offset in cents to a frequency in Hz
163inline double addCents (double baseFrequencyHz, double cents)
164{
165 return baseFrequencyHz * std::pow (2.0, cents / 1200.0);
166}
167
168/// @brief Keeps phase in 0..twoPi range
169template <typename SampleType>
170SampleType wrapPhase (const SampleType phaseRadians)
171{
172 SampleType wrappedPhaseRadians = std::remainder (phaseRadians, (SampleType) hart::twoPi);
173
174 if (wrappedPhaseRadians < 0.0)
175 wrappedPhaseRadians += hart::twoPi;
176
177 return wrappedPhaseRadians;
178}
179
180/// @brief Finds next power of 2 after a non-negative number x
181static size_t nextPowerOfTwo (size_t x)
182{
183 size_t power = 1;
184
185 while (power < x)
186 power <<= 1;
187
188 return power;
189}
190
191/// @brief Finds previous power of 2 after a non-negative number x
192/// @note If x is a power of 2 itself, it will return x
193static size_t previousPowerOfTwo (size_t x)
194{
195 if (x == 0)
196 return 0;
197
198 size_t power = 1;
199
200 while ((power << 1) < x)
201 power <<= 1;
202
203 return power;
204}
205
206// @brief Checks if number is a power of 2
207static bool isPowerOfTwo (size_t x)
208{
209 return (x != 0) && ((x & (x - 1)) == 0);
210}
211
212// @brief Check if file exists and whether it's possible to read it
213// @note There are better ways to do it post-C++17, but HART is C++11.
214inline static bool fileExistsAndReadable (const std::string& path)
215{
216 std::ifstream file (path.c_str());
217 return file.good();
218}
219
220/// @brief Checks if the provided file path is absolute
221inline static bool isAbsolutePath (const std::string& path)
222{
223 if (path.empty())
224 return false;
225
226 if (path[0] == '/' || path[0] == '\\')
227 return true;
228
229 #ifdef _WIN32
230 if (path.size() > 1 && std::isalpha (path[0]) && path[1] == ':')
231 return true;
232 #endif
233
234 return false;
235}
236
237/// @brief Converts path to absolute, if it's relative
238/// @details Relative paths are resolved based on a provided `--data-root-path` CLI argument
239inline static std::string toAbsolutePath (const std::string& path)
240{
241 if (isAbsolutePath(path))
242 return path;
243
245}
246
247/// @brief `std::unordered_map::contains()` replacement for C++11
248template <typename KeyType, typename ValueType>
249inline static bool contains (const std::unordered_map<KeyType, ValueType>& map, const KeyType& key)
250{
251 return map.find (key) != map.end();
252}
253
254/// @brief `std::make_unique()` replacement for C++11
255/// @details For C++11 compatibility only. If you're one C++14 or later, just use STL version.
256template<typename ObjectType, typename... Args>
257std::unique_ptr<ObjectType> make_unique (Args&&... args)
258{
259 return std::unique_ptr<ObjectType> (new ObjectType (std::forward<Args> (args)...));
260}
261
262/// @brief Returns `true` if an exception is currently being unwound
263inline static bool isExceptionUnwinding()
264{
265#if defined(__cpp_lib_uncaught_exceptions)
266 return std::uncaught_exceptions() > 0;
267#else
268 return std::uncaught_exception();
269#endif
270}
271
272/// @brief Defines a basic string representation of your class
273/// @details If your class takes ctor arguments, it's strongly encouraged to make a proper
274/// implementation of `represent()`, so that you get more detailed test failure reports.
275/// See @ref hart::DSP::represent(), @ref hart::Matcher::represent(),
276/// @ref hart::Signal::represent() for the description.
277#define HART_DEFINE_GENERIC_REPRESENT(ClassName)
278 virtual void represent(std::ostream& stream) const override
279 {
280 stream << #ClassName "()";
281 }
282
283/// @private
284#if defined(__GNUC__) || defined(__clang__)
285 #define HART_DEPRECATED(msg) __attribute__((deprecated(msg)))
286#elif defined(_MSC_VER)
287 #define HART_DEPRECATED(msg) __declspec(deprecated(msg))
288#else
289 #define HART_DEPRECATED(msg)
290#endif
291
292} // namespace hart
constexpr double twoPi
2 * pi
static bool isAbsolutePath(const std::string &path)
Checks if the provided file path is absolute.
FloatType nan()
Returns a quiet NaN value for the given floating-point type.
Channel
Helper values for channel indices.
static bool isPowerOfTwo(size_t x)
SampleType wrapPhase(const SampleType phaseRadians)
Keeps phase in 0..twoPi range.
static bool contains(const std::unordered_map< KeyType, ValueType > &map, const KeyType &key)
std::unordered_map::contains() replacement for C++11
constexpr double halfPi
pi / 2
static size_t roundToSizeT(SampleType x)
Rounds a floating point value to a size_t value.
constexpr double inf
Infinity.
static SampleType ratioToDecibels(SampleType valueLinear)
Converts linear value (ratio) to dB.
static SampleType floatsNotEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
MidSideChannel
Helper values for mid-side channel indices.
Interpolation
Interpolation method.
std::unique_ptr< ObjectType > make_unique(Args &&... args)
std::make_unique() replacement for C++11
static SampleType powerToDecibels(SampleType valueLinear)
Converts linear value (power) to dB.
double addCents(double baseFrequencyHz, double cents)
Anns an offset in cents to a frequency in Hz.
static size_t nextPowerOfTwo(size_t x)
Finds next power of 2 after a non-negative number x.
NumericType clamp(const NumericType &value, const NumericType &low, const NumericType &high)
std::clamp() replacement for C++11
constexpr double oo
Infinity.
static bool fileExistsAndReadable(const std::string &path)
static std::string toAbsolutePath(const std::string &path)
Converts path to absolute, if it's relative.
static SampleType decibelsToRatio(SampleType valueDb)
Converts dB to linear value (ratio)
static size_t previousPowerOfTwo(size_t x)
Finds previous power of 2 after a non-negative number x.
static bool isExceptionUnwinding()
Returns true if an exception is currently being unwound.
constexpr double pi
pi
static SampleType floatsEqual(SampleType a, SampleType b, SampleType epsilon=(SampleType) 1e-8)
Compares two floating point numbers within a given tolerance.
static SampleType decibelsToPower(SampleType valueDb)
Converts dB to linear value (power)
Oversampling
Oversampling ratio.
Loop
Helper values for something that could loop, like a Signal.
Holds values set by the user via CLI interface.
std::string getDataRootPath()
Get data root path set by a "`--data-root-path`,`-d`" argument.
static CLIConfig & getInstance()
Get the singleton instance.