LMMS
Loading...
Searching...
No Matches
lmms_math.h
Go to the documentation of this file.
1/*
2 * lmms_math.h - defines math functions
3 *
4 * Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program (see COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 *
23 */
24
25#ifndef LMMS_MATH_H
26#define LMMS_MATH_H
27
28#include <QtGlobal>
29#include <algorithm>
30#include <cassert>
31#include <cmath>
32#include <cstdint>
33#include <cstring>
34#include <numbers>
35#include <concepts>
36
37#include "lmms_constants.h"
38
39#ifdef __SSE2__
40#include <emmintrin.h>
41#endif
42
43namespace lmms
44{
45
46
47// TODO C++23: Make constexpr since std::abs() will be constexpr
48inline bool approximatelyEqual(float x, float y) noexcept
49{
50 return x == y || std::abs(x - y) < F_EPSILON;
51}
52
53// TODO C++23: Make constexpr since std::trunc() will be constexpr
63inline auto fraction(std::floating_point auto x) noexcept
64{
65 return x - std::trunc(x);
66}
67
68
69// TODO C++23: Make constexpr since std::floor() will be constexpr
80inline auto absFraction(std::floating_point auto x) noexcept
81{
82 return x - std::floor(x);
83}
84
85
88inline int fastRand() noexcept
89{
90 thread_local unsigned long s_next = 1;
91 s_next = s_next * 1103515245 + 12345;
92 return s_next / 65536 % 32768;
93}
94
95
98template<std::floating_point T>
99inline T fastRand(T upper) noexcept
100{
101 constexpr auto FAST_RAND_RATIO = static_cast<T>(1.0 / 32768);
102 return fastRand() * upper * FAST_RAND_RATIO;
103}
104
105
109template<std::integral T>
110inline T fastRand(T upper) noexcept
111{
112 constexpr float FAST_RAND_RATIO = 1.f / 32768;
113 return static_cast<T>(fastRand() * static_cast<float>(upper) * FAST_RAND_RATIO);
114}
115
116
119template<typename T> requires std::is_arithmetic_v<T>
120inline auto fastRand(T from, T to) noexcept { return from + fastRand(to - from); }
121
122
125template<std::floating_point T>
126inline T fastRandInc(T upper) noexcept
127{
128 constexpr auto FAST_RAND_RATIO = static_cast<T>(1.0 / 32767);
129 return fastRand() * upper * FAST_RAND_RATIO;
130}
131
132
135template<std::unsigned_integral T>
136inline T fastRandInc(T upper) noexcept
137{
138 // The integer specialization of this function is kind of weird, but it is
139 // necessary to prevent massive bias away from the maximum value.
140 // FAST_RAND_RATIO here is 1 greater than normal, so when multiplied by, it
141 // will result in a random float within [0, @p upper) instead of the usual
142 // [0, @p upper].
143 constexpr float FAST_RAND_RATIO = 1.f / 32768;
144 // Since the random float will be in a range that does not include @upper
145 // due to the above ratio, increase the upper bound by 1.
146 // All values greater than @p upper get rounded down to @p upper, making the
147 // chance of returning a value of @p upper the same as any other of the
148 // possible values.
149 // No need to copysign() unlike the signed_integral overload, since it will always be positive
150 return static_cast<T>(fastRand() * (upper + 1.f) * FAST_RAND_RATIO);
151}
152
153
157template<std::signed_integral T>
158inline T fastRandInc(T upper) noexcept
159{
160 // The integer specialization of this function is kind of weird, but it is
161 // necessary to prevent massive bias away from the maximum value.
162 // FAST_RAND_RATIO here is 1 greater than normal, so when multiplied by, it
163 // will result in a random float within [0, @p upper) instead of the usual
164 // [0, @p upper].
165 constexpr float FAST_RAND_RATIO = 1.f / 32768;
166 // Since the random float will be in a range that does not include @upper
167 // due to the above ratio, increase the magnitude of the upper bound by 1.
168 // All values greater than @p upper get rounded down to @p upper, making the
169 // chance of returning a value of @p upper the same as any other of the
170 // possible values.
171 // HACK: Even on -O3, without this static_cast, it will convert @p upper to float twice for some reason
172 const auto fupper = static_cast<float>(upper);
173 const float r = fupper + std::copysign(1.f, fupper);
174 // Always round towards 0 (implicit truncation occurs during static_cast).
175 return static_cast<T>(fastRand() * r * FAST_RAND_RATIO);
176}
177
178
183template<typename T> requires std::is_arithmetic_v<T>
184inline auto fastRandInc(T from, T to) noexcept { return from + fastRandInc(to - from); }
185
186
188inline bool oneIn(unsigned chance) noexcept { return 0 == (fastRand() % chance); }
189
190
192template<class T>
193static void roundAt(T& value, const T& where, const T& stepSize)
194{
195 if (std::abs(value - where) < F_EPSILON * std::abs(stepSize))
196 {
197 value = where;
198 }
199}
200
202inline double fastPow(double a, double b)
203{
204 double d;
205 std::int32_t x[2];
206
207 std::memcpy(x, &a, sizeof(x));
208 x[1] = static_cast<std::int32_t>(b * (x[1] - 1072632447) + 1072632447);
209 x[0] = 0;
210
211 std::memcpy(&d, x, sizeof(d));
212 return d;
213}
214
215
217template<typename T>
218constexpr T sign(T val) noexcept
219{
220 return val >= 0 ? 1 : -1;
221}
222
223
225inline float sqrt_neg(float val)
226{
227 return std::sqrt(std::abs(val)) * sign(val);
228}
229
231inline float signedPowf(float v, float e)
232{
233 return std::pow(std::abs(v), e) * sign(v);
234}
235
236
239inline float logToLinearScale(float min, float max, float value)
240{
241 using namespace std::numbers;
242 if (min < 0)
243 {
244 const float mmax = std::max(std::abs(min), std::abs(max));
245 const float val = value * (max - min) + min;
246 float result = signedPowf(val / mmax, e_v<float>) * mmax;
247 return std::isnan(result) ? 0 : result;
248 }
249 float result = std::pow(value, e_v<float>) * (max - min) + min;
250 return std::isnan(result) ? 0 : result;
251}
252
253
255inline float linearToLogScale(float min, float max, float value)
256{
257 constexpr auto inv_e = static_cast<float>(1.0 / std::numbers::e);
258 const float valueLimited = std::clamp(value, min, max);
259 const float val = (valueLimited - min) / (max - min);
260 if (min < 0)
261 {
262 const float mmax = std::max(std::abs(min), std::abs(max));
263 float result = signedPowf(valueLimited / mmax, inv_e) * mmax;
264 return std::isnan(result) ? 0 : result;
265 }
266 float result = std::pow(val, inv_e) * (max - min) + min;
267 return std::isnan(result) ? 0 : result;
268}
269
270
271// TODO C++26: Make constexpr since std::exp() will be constexpr
272template<typename T> requires std::is_arithmetic_v<T>
273inline auto fastPow10f(T x)
274{
275 using F_T = std::conditional_t<std::is_floating_point_v<T>, T, float>;
276 return std::exp(std::numbers::ln10_v<F_T> * x);
277}
278
279
280// TODO C++26: Make constexpr since std::log() will be constexpr
281template<typename T> requires std::is_arithmetic_v<T>
282inline auto fastLog10f(T x)
283{
284 using F_T = std::conditional_t<std::is_floating_point_v<T>, T, float>;
285 constexpr auto inv_ln10 = static_cast<F_T>(1.0 / std::numbers::ln10);
286 return std::log(x) * inv_ln10;
287}
288
289
293inline float ampToDbfs(float amp)
294{
295 return fastLog10f(amp) * 20.0f;
296}
297
298
302inline float dbfsToAmp(float dbfs)
303{
304 return fastPow10f(dbfs * 0.05f);
305}
306
307
311inline float safeAmpToDbfs(float amp)
312{
313 return amp == 0.0f ? -INFINITY : ampToDbfs(amp);
314}
315
316
320inline float safeDbfsToAmp(float dbfs)
321{
322 return std::isinf(dbfs) ? 0.0f : dbfsToAmp(dbfs);
323}
324
325
326// TODO C++20: use std::formatted_size
327// @brief Calculate number of digits which LcdSpinBox would show for a given number
328inline int numDigitsAsInt(float f)
329{
330 // use rounding:
331 // LcdSpinBox sometimes uses std::round(), sometimes cast rounding
332 // we use rounding to be on the "safe side"
333 int asInt = static_cast<int>(std::round(f));
334 int digits = 1; // always at least 1
335 if(asInt < 0)
336 {
337 ++digits;
338 asInt = -asInt;
339 }
340 // "asInt" is positive from now
341 int power = 1;
342 for (int i = 1; i < 10; ++i)
343 {
344 power *= 10;
345 if (asInt >= power) { ++digits; } // 2 digits for >=10, 3 for >=100
346 else { break; }
347 }
348 return digits;
349}
350
351template <typename T>
353{
354public:
355 LinearMap(T x1, T y1, T x2, T y2)
356 {
357 T const dx = x2 - x1;
358 assert (dx != T(0));
359
360 m_a = (y2 - y1) / dx;
361 m_b = y1 - m_a * x1;
362 }
363
364 T map(T x) const
365 {
366 return m_a * x + m_b;
367 }
368
369private:
372};
373
374#ifdef __SSE2__
375// exp approximation for SSE2: https://stackoverflow.com/a/47025627/5759631
376// Maximum relative error of 1.72863156e-3 on [-87.33654, 88.72283]
377inline __m128 fastExp(__m128 x)
378{
379 __m128 f, p, r;
380 __m128i t, j;
381 const __m128 a = _mm_set1_ps (12102203.0f); /* (1 << 23) / log(2) */
382 const __m128i m = _mm_set1_epi32 (0xff800000); /* mask for integer bits */
383 const __m128 ttm23 = _mm_set1_ps (1.1920929e-7f); /* exp2(-23) */
384 const __m128 c0 = _mm_set1_ps (0.3371894346f);
385 const __m128 c1 = _mm_set1_ps (0.657636276f);
386 const __m128 c2 = _mm_set1_ps (1.00172476f);
387
388 t = _mm_cvtps_epi32 (_mm_mul_ps (a, x));
389 j = _mm_and_si128 (t, m); /* j = (int)(floor (x/log(2))) << 23 */
390 t = _mm_sub_epi32 (t, j);
391 f = _mm_mul_ps (ttm23, _mm_cvtepi32_ps (t)); /* f = (x/log(2)) - floor (x/log(2)) */
392 p = c0; /* c0 */
393 p = _mm_mul_ps (p, f); /* c0 * f */
394 p = _mm_add_ps (p, c1); /* c0 * f + c1 */
395 p = _mm_mul_ps (p, f); /* (c0 * f + c1) * f */
396 p = _mm_add_ps (p, c2); /* p = (c0 * f + c1) * f + c2 ~= 2^f */
397 r = _mm_castsi128_ps (_mm_add_epi32 (j, _mm_castps_si128 (p))); /* r = p * 2^i*/
398 return r;
399}
400
401// Lost Robot's SSE2 adaptation of Kari's vectorized log approximation: https://stackoverflow.com/a/65537754/5759631
402// Maximum relative error of 7.922410e-4 on [1.0279774e-38f, 3.4028235e+38f]
403inline __m128 fastLog(__m128 a)
404{
405 __m128i aInt = _mm_castps_si128(a);
406 __m128i e = _mm_sub_epi32(aInt, _mm_set1_epi32(0x3f2aaaab));
407 e = _mm_and_si128(e, _mm_set1_epi32(0xff800000));
408 __m128i subtr = _mm_sub_epi32(aInt, e);
409 __m128 m = _mm_castsi128_ps(subtr);
410 __m128 i = _mm_mul_ps(_mm_cvtepi32_ps(e), _mm_set1_ps(1.19209290e-7f));
411 __m128 f = _mm_sub_ps(m, _mm_set1_ps(1.0f));
412 __m128 s = _mm_mul_ps(f, f);
413 __m128 r = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(0.230836749f), f), _mm_set1_ps(-0.279208571f));
414 __m128 t = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(0.331826031f), f), _mm_set1_ps(-0.498910338f));
415 r = _mm_add_ps(_mm_mul_ps(r, s), t);
416 r = _mm_add_ps(_mm_mul_ps(r, s), f);
417 r = _mm_add_ps(_mm_mul_ps(i, _mm_set1_ps(0.693147182f)), r);
418 return r;
419}
420
421inline __m128 sse2Abs(__m128 x)
422{
423 return _mm_and_ps(x, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)));// clear sign bit
424}
425
426inline __m128 sse2Floor(__m128 x)
427{
428 __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(x)); // trunc toward 0
429 __m128 needs_correction = _mm_cmplt_ps(x, t); // checks if x < trunc
430 return _mm_sub_ps(t, _mm_and_ps(needs_correction, _mm_set1_ps(1.0f)));
431}
432
433inline __m128 sse2Round(__m128 x)
434{
435 __m128 sign_mask = _mm_cmplt_ps(x, _mm_setzero_ps());// checks if x < 0
436 __m128 bias_pos = _mm_set1_ps(0.5f);
437 __m128 bias_neg = _mm_set1_ps(-0.5f);
438 __m128 bias = _mm_or_ps(_mm_and_ps(sign_mask, bias_neg), _mm_andnot_ps(sign_mask, bias_pos));
439 __m128 y = _mm_add_ps(x, bias);
440 return _mm_cvtepi32_ps(_mm_cvttps_epi32(y));
441}
442
443#endif // __SSE2__
444
445} // namespace lmms
446
447#endif // LMMS_MATH_H
#define noexcept
Definition DistrhoDefines.h:72
assert(0)
uint8_t a
Definition Spc_Cpu.h:141
LinearMap(T x1, T y1, T x2, T y2)
Definition lmms_math.h:355
T map(T x) const
Definition lmms_math.h:364
T m_a
Definition lmms_math.h:370
T m_b
Definition lmms_math.h:371
* e
Definition inflate.c:1404
unsigned * m
Definition inflate.c:1559
struct huft * t
Definition inflate.c:943
register unsigned j
Definition inflate.c:1576
int y
Definition inflate.c:1588
unsigned v[N_MAX]
Definition inflate.c:1584
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned x[BMAX+1]
Definition inflate.c:1586
unsigned f
Definition inflate.c:1572
static void c2(register WDL_FFT_COMPLEX *a)
Definition fft.c:270
static PuglViewHint int value
Definition pugl.h:1708
int val
Definition jpeglib.h:956
Definition AudioAlsa.cpp:35
float linearToLogScale(float min, float max, float value)
Scales value from logarithmic to linear. Value should be in min-max range.
Definition lmms_math.h:255
float safeAmpToDbfs(float amp)
Converts linear amplitude (0-1.0) to dBFS scale. Handles zeroes as -inf.
Definition lmms_math.h:311
float dbfsToAmp(float dbfs)
Converts dBFS-scale to linear amplitude with 0dBFS = 1.0.
Definition lmms_math.h:302
T fastRandInc(T upper) noexcept
Returns a pseudorandom number within [0, upper] (inclusive upper bound).
Definition lmms_math.h:126
int numDigitsAsInt(float f)
Definition lmms_math.h:328
float signedPowf(float v, float e)
Exponential function that deals with negative bases.
Definition lmms_math.h:231
float sqrt_neg(float val)
if val >= 0.0f, returns sqrt(val), else: -sqrt(-val)
Definition lmms_math.h:225
float logToLinearScale(float min, float max, float value)
Scales @value from linear to logarithmic. Value should be within [0,1].
Definition lmms_math.h:239
double fastPow(double a, double b)
Source: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/.
Definition lmms_math.h:202
static void roundAt(T &value, const T &where, const T &stepSize)
Round value to where depending on step size.
Definition lmms_math.h:193
auto absFraction(std::floating_point auto x) noexcept
Returns the wrapped fractional part of a float, a value between 0.0f and 1.0f.
Definition lmms_math.h:80
float ampToDbfs(float amp)
Converts linear amplitude (>0-1.0) to dBFS scale.
Definition lmms_math.h:293
float safeDbfsToAmp(float dbfs)
Converts dBFS-scale to linear amplitude with 0dBFS = 1.0. Handles infinity as zero.
Definition lmms_math.h:320
int fastRand() noexcept
Returns a pseudorandom integer within [0, 32768).
Definition lmms_math.h:88
constexpr float F_EPSILON
Definition lmms_constants.h:35
auto fastLog10f(T x)
Definition lmms_math.h:282
constexpr T sign(T val) noexcept
returns +1 if val >= 0, else -1
Definition lmms_math.h:218
bool approximatelyEqual(float x, float y) noexcept
Definition lmms_math.h:48
auto fraction(std::floating_point auto x) noexcept
Returns the fractional part of a float, a value between -1.0f and 1.0f.
Definition lmms_math.h:63
bool oneIn(unsigned chance) noexcept
Returns true one in chance times at random.
Definition lmms_math.h:188
auto fastPow10f(T x)
Definition lmms_math.h:273
#define min(x, y)
Definition os.h:74
#define max(x, y)
Definition os.h:78
#define INFINITY
Definition serd_test.c:29
uch * p
Definition crypt.c:594
int r
Definition crypt.c:458
b
Definition crypt.c:628
int result
Definition process.c:1455