summaryrefslogtreecommitdiff
path: root/cras/src/dsp/drc_math.h
blob: 970b72958c8379fd06a5591fa11475a0ad03ae6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef DRC_MATH_H_
#define DRC_MATH_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>
#include <math.h>

#ifndef __BIONIC__
#include <ieee754.h>
#else
union ieee754_float {
	float f;
	/* Little endian float fields */
	struct {
		unsigned int mantissa : 23;
		unsigned int exponent : 8;
		unsigned int negative : 1;
	} ieee;
};
#endif

/* Uncomment to use the slow but accurate functions. */
/* #define SLOW_DB_TO_LINEAR */
/* #define SLOW_LINEAR_TO_DB */
/* #define SLOW_WARP_SIN */
/* #define SLOW_KNEE_EXP */
/* #define SLOW_FREXPF */

#define PI_FLOAT 3.141592653589793f
#define PI_OVER_TWO_FLOAT 1.57079632679489661923f
#define TWO_OVER_PI_FLOAT 0.63661977236758134f
#define NEG_TWO_DB 0.7943282347242815f /* -2dB = 10^(-2/20) */

#ifndef max
#define max(a, b)                                                              \
	({                                                                     \
		__typeof__(a) _a = (a);                                        \
		__typeof__(b) _b = (b);                                        \
		_a > _b ? _a : _b;                                             \
	})
#endif

#ifndef min
#define min(a, b)                                                              \
	({                                                                     \
		__typeof__(a) _a = (a);                                        \
		__typeof__(b) _b = (b);                                        \
		_a < _b ? _a : _b;                                             \
	})
#endif

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define PURE __attribute__((pure))
static inline float decibels_to_linear(float decibels) PURE;
static inline float linear_to_decibels(float linear) PURE;
static inline float warp_sinf(float x) PURE;
static inline float warp_asinf(float x) PURE;
static inline float knee_expf(float input) PURE;

extern float db_to_linear[201]; /* from -100dB to 100dB */

void drc_math_init();

/* Rounds the input number to the nearest integer */
#if defined(__arm__)
static inline float round_int(float x)
{
	return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f);
}
#else
#define round_int rintf /* Uses frintx for arm64 and roundss for SSE4.1 */
#endif

static inline float decibels_to_linear(float decibels)
{
#ifdef SLOW_DB_TO_LINEAR
	/* 10^(x/20) = e^(x * log(10^(1/20))) */
	return expf(0.1151292546497022f * decibels);
#else
	float x;
	float fi;
	int i;

	fi = round_int(decibels);
	x = decibels - fi;
	i = (int)fi;
	i = max(min(i, 100), -100);

	/* Coefficients obtained from:
	 * fpminimax(10^(x/20), [|1,2,3|], [|SG...|], [-0.5;0.5], 1, absolute);
	 * max error ~= 7.897e-8
	 */
	const float A3 = 2.54408805631101131439208984375e-4f;
	const float A2 = 6.628888659179210662841796875e-3f;
	const float A1 = 0.11512924730777740478515625f;
	const float A0 = 1.0f;

	float x2 = x * x;
	return ((A3 * x + A2) * x2 + (A1 * x + A0)) * db_to_linear[i + 100];
#endif
}

static inline float frexpf_fast(float x, int *e)
{
#ifdef SLOW_FREXPF
	return frexpf(x, e);
#else
	union ieee754_float u;
	u.f = x;
	int exp = u.ieee.exponent;
	if (exp == 0xff)
		return NAN;
	*e = exp - 126;
	u.ieee.exponent = 126;
	return u.f;
#endif
}

static inline float linear_to_decibels(float linear)
{
	/* For negative or zero, just return a very small dB value. */
	if (linear <= 0)
		return -1000;

#ifdef SLOW_LINEAR_TO_DB
	/* 20 * log10(x) = 20 / log(10) * log(x) */
	return 8.6858896380650366f * logf(linear);
#else
	int e = 0;
	float x = frexpf_fast(linear, &e);
	float exp = e;

	if (x > 0.707106781186548f) {
		x *= 0.707106781186548f;
		exp += 0.5f;
	}

	/* Coefficients obtained from:
	 * fpminimax(log10(x), 5, [|SG...|], [1/2;sqrt(2)/2], absolute);
	 * max err ~= 6.088e-8
	 */
	const float A5 = 1.131880283355712890625f;
	const float A4 = -4.258677959442138671875f;
	const float A3 = 6.81631565093994140625f;
	const float A2 = -6.1185703277587890625f;
	const float A1 = 3.6505267620086669921875f;
	const float A0 = -1.217894077301025390625f;

	float x2 = x * x;
	float x4 = x2 * x2;
	return ((A5 * x + A4) * x4 + (A3 * x + A2) * x2 + (A1 * x + A0)) *
		       20.0f +
	       exp * 6.0205999132796239f;
#endif
}

static inline float warp_sinf(float x)
{
#ifdef SLOW_WARP_SIN
	return sinf(PI_OVER_TWO_FLOAT * x);
#else
	/* Coefficients obtained from:
	 * fpminimax(sin(x*pi/2), [|1,3,5,7|], [|SG...|], [-1e-30;1], absolute)
	 * max err ~= 5.901e-7
	 */
	const float A7 = -4.3330336920917034149169921875e-3f;
	const float A5 = 7.9434238374233245849609375e-2f;
	const float A3 = -0.645892798900604248046875f;
	const float A1 = 1.5707910060882568359375f;

	float x2 = x * x;
	float x4 = x2 * x2;
	return x * ((A7 * x2 + A5) * x4 + (A3 * x2 + A1));
#endif
}

static inline float warp_asinf(float x)
{
	return asinf(x) * TWO_OVER_PI_FLOAT;
}

static inline float knee_expf(float input)
{
#ifdef SLOW_KNEE_EXP
	return expf(input);
#else
	/* exp(x) = decibels_to_linear(20*log10(e)*x) */
	return decibels_to_linear(8.685889638065044f * input);
#endif
}

/* Returns 1 for nan or inf, 0 otherwise. This is faster than the alternative
 * return x != 0 && !isnormal(x);
 */
static inline int isbadf(float x)
{
	union ieee754_float u;
	u.f = x;
	return u.ieee.exponent == 0xff;
}

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* DRC_MATH_H_ */