/* 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> /* 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(); union ieee754_float { float f; /* This is the IEEE 754 single-precision format. */ struct { /* Little endian. */ unsigned int mantissa:23; unsigned int exponent:8; unsigned int negative:1; } ieee; }; /* Rounds the input number to the nearest integer */ #ifdef __arm__ static inline float round_int(float x) { return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f); } #else #define round_int rintf /* glibc will use roundss if SSE4.1 is available */ #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_ */