Skip to content

Commit

Permalink
DRC: math: Replace exponential function for performance
Browse files Browse the repository at this point in the history
For DRC performance, replace "exp_small_fixed()" with "sofm_exp_int32()".
Included supporting change to include "sofm_exp_int32()" within exp_fixed()
and removed "exp_small_fixed()" from future use.

Signed-off-by: shastry <[email protected]>
  • Loading branch information
ShriramShastry committed Nov 24, 2023
1 parent 1b81841 commit 06bc952
Show file tree
Hide file tree
Showing 19 changed files with 226 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/audio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ config COMP_DRC
bool "Dynamic Range Compressor component"
select CORDIC_FIXED
select NUMBERS_NORM
select MATH_DECIBELS
select COMP_BLOB
select MATH_EXP
default n
help
Select for Dynamic Range Compressor (DRC) component. A DRC can be used
Expand Down
9 changes: 6 additions & 3 deletions src/audio/drc/drc_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <sof/audio/drc/drc_math.h>
#include <sof/audio/format.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/numbers.h>
#include <stdint.h>

Expand All @@ -35,7 +36,7 @@ static int32_t knee_curveK(const struct sof_drc_params *p, int32_t x)
* beta = -expf(k * linear_threshold) / k
* gamma = -k * x
*/
knee_exp_gamma = exp_fixed(Q_MULTSR_32X32((int64_t)x, -p->K, 31, 20, 27)); /* Q12.20 */
knee_exp_gamma = sofm_exp_fixed(Q_MULTSR_32X32((int64_t)x, -p->K, 31, 20, 27)); /* Q12.20 */
return p->knee_alpha + Q_MULTSR_32X32((int64_t)p->knee_beta, knee_exp_gamma, 24, 20, 24);
}

Expand Down Expand Up @@ -65,8 +66,10 @@ static int32_t volume_gain(const struct sof_drc_params *p, int32_t x)
* => y/x = ratio_base * x^(s - 1)
* => y/x = ratio_base * e^(log(x) * (s - 1))
*/
exp_knee = exp_fixed(Q_MULTSR_32X32((int64_t)drc_log_fixed(Q_SHIFT_RND(x, 31, 26)),
(p->slope - ONE_Q30), 26, 30, 27)); /* Q12.20 */
exp_knee = sofm_exp_fixed(
Q_MULTSR_32X32((int64_t)
drc_log_fixed(Q_SHIFT_RND(x, 31, 26)),
(p->slope - ONE_Q30), 26, 30, 27)); /* Q12.20 */
y = Q_MULTSR_32X32((int64_t)p->ratio_base, exp_knee, 30, 20, 30);
}

Expand Down
5 changes: 3 additions & 2 deletions src/audio/drc/drc_hifi3.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <sof/audio/drc/drc_math.h>
#include <sof/audio/format.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/numbers.h>
#include <stdint.h>

Expand Down Expand Up @@ -41,7 +42,7 @@ static int32_t knee_curveK(const struct sof_drc_params *p, int32_t x)
* gamma = -k * x
*/
gamma = drc_mult_lshift(x, -p->K, drc_get_lshift(31, 20, 27));
knee_exp_gamma = exp_fixed(gamma);
knee_exp_gamma = sofm_exp_fixed(gamma);
knee_curve_k = drc_mult_lshift(p->knee_beta, knee_exp_gamma, drc_get_lshift(24, 20, 24));
knee_curve_k = AE_ADD32(knee_curve_k, p->knee_alpha);
return knee_curve_k;
Expand Down Expand Up @@ -77,7 +78,7 @@ static int32_t volume_gain(const struct sof_drc_params *p, int32_t x)
tmp = AE_SRAI32R(x, 5); /* Q1.31 -> Q5.26 */
tmp = drc_log_fixed(tmp); /* Q6.26 */
tmp2 = AE_SUB32(p->slope, ONE_Q30); /* Q2.30 */
exp_knee = exp_fixed(drc_mult_lshift(tmp, tmp2, drc_get_lshift(26, 30, 27)));
exp_knee = sofm_exp_fixed(drc_mult_lshift(tmp, tmp2, drc_get_lshift(26, 30, 27)));
y = drc_mult_lshift(p->ratio_base, exp_knee, drc_get_lshift(30, 20, 30));
}

Expand Down
7 changes: 3 additions & 4 deletions src/audio/drc/drc_math_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <sof/audio/drc/drc_math.h>
#include <sof/audio/format.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/numbers.h>
#include <sof/math/trig.h>

Expand Down Expand Up @@ -219,8 +220,6 @@ inline int32_t drc_inv_fixed(int32_t x, int32_t precision_x, int32_t precision_y
#undef qc
}

#endif /* DRC_GENERIC */

/*
* Input x is Q6.26; valid range: (0.0, 32.0); x <= 0 is not supported
* y is Q2.30: (-2.0, 2.0)
Expand All @@ -233,8 +232,8 @@ inline int32_t drc_pow_fixed(int32_t x, int32_t y)
return 0;

/* x^y = expf(y * log(x)) */
return exp_fixed(q_mult(y, drc_log_fixed(x), 30, 26, 27));
return sofm_exp_fixed(q_mult(y, drc_log_fixed(x), 30, 26, 27));
}

#endif
#undef q_multq
#undef q_mult
2 changes: 2 additions & 0 deletions src/audio/mfcc/mfcc_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <sof/audio/component.h>
#include <sof/audio/audio_stream.h>
#include <sof/math/auditory.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/trig.h>
#include <sof/math/window.h>
#include <sof/trace/trace.h>
Expand Down
2 changes: 1 addition & 1 deletion src/audio/multiband_drc/multiband_drc.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ static int multiband_drc_init(struct processing_module *mod)
*/
cd->process_enabled = true;
#else
cd->process_enabled = false;
cd->process_enabled = true;
#endif

/* Handler for configuration data */
Expand Down
6 changes: 0 additions & 6 deletions src/include/sof/math/decibels.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@

#include <stdint.h>

#define EXP_FIXED_INPUT_QY 27
#define EXP_FIXED_OUTPUT_QY 20
#define DB2LIN_FIXED_INPUT_QY 24
#define DB2LIN_FIXED_OUTPUT_QY 20

int32_t exp_fixed(int32_t x); /* Input is Q5.27, output is Q12.20 */
int32_t db2lin_fixed(int32_t x); /* Input is Q8.24, output is Q12.20 */

#endif /* __SOF_MATH_DECIBELS_H__ */
4 changes: 2 additions & 2 deletions src/include/sof/math/exp_fcn.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@

#endif

int32_t sofm_exp_int32(int32_t x);

int32_t sofm_exp_int32(int32_t x); /* Input is Q4.28, Output is Q9.23 */
int32_t sofm_exp_fixed(int32_t x); /* Input is Q5.27, output is Q12.20 */
#endif
9 changes: 9 additions & 0 deletions src/math/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ config MATH_EXP
an input range of -5 to +5 gives positive numbers between 0.00673794699908547 and
148.413159102577. The precision of this function is 1e-4.

config MATH_EXP_SMALL_FXD
bool "Small Exponential functions"
default n
help
By selecting this, the 32-bit exp_small_fixed() function can be used to calculate
exponential values. With a mean thdn of -131.205(dBc), an exponential function with
an input range of -2 to +2 gives positive numbers between 0.135335255 and
7.38905609. The precision of this function is 1e-6.

config NATURAL_LOGARITHM_FIXED
bool "Natural Logarithm function"
default n
Expand Down
3 changes: 2 additions & 1 deletion src/math/auditory/auditory.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <rtos/alloc.h>
#include <sof/math/auditory.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/fft.h>
#include <sof/math/log.h>
#include <sof/math/numbers.h>
Expand Down Expand Up @@ -80,7 +81,7 @@ int16_t psy_mel_to_hz(int16_t mel)
return 0;

exp_arg = Q_MULTSR_32X32((int64_t)mel, ONE_OVER_MELDIV_Q31, 2, 31, 27);
exp = exp_fixed(exp_arg) - ONE_Q20;
exp = sofm_exp_fixed(exp_arg) - ONE_Q20;
hz = Q_MULTSR_32X32((int64_t)exp, 700, 20, 0, 0);
return hz;
}
Expand Down
85 changes: 2 additions & 83 deletions src/math/decibels.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,11 @@

#include <sof/audio/format.h>
#include <sof/math/decibels.h>
#include <sof/math/exp_fcn.h>
#include <stdint.h>

#define ONE_Q20 Q_CONVERT_FLOAT(1.0, 20) /* Use Q12.20 */
#define ONE_Q23 Q_CONVERT_FLOAT(1.0, 23) /* Use Q9.23 */
#define TWO_Q27 Q_CONVERT_FLOAT(2.0, 27) /* Use Q5.27 */
#define MINUS_TWO_Q27 Q_CONVERT_FLOAT(-2.0, 27) /* Use Q5.27 */
#define LOG10_DIV20_Q27 Q_CONVERT_FLOAT(0.1151292546, 27) /* Use Q5.27 */

/* Exponent function for small values of x. This function calculates
* fairly accurately exponent for x in range -2.0 .. +2.0. The iteration
* uses first 11 terms of Taylor series approximation for exponent
* function. With the current scaling the numerator just remains under
* 64 bits with the 11 terms.
*
* See https://en.wikipedia.org/wiki/Exponential_function#Computation
*
* The input is Q3.29
* The output is Q9.23
*/

static int32_t exp_small_fixed(int32_t x)
{
int64_t p;
int64_t num = Q_SHIFT_RND(x, 29, 23);
int32_t y0 = (int32_t)num;
int32_t den = 1;
int32_t inc;
int k;

/* Numerator is x^k, denominator is k! */
for (k = 2; k < 12; k++) {
p = num * x; /* Q9.23 x Q3.29 -> Q12.52 */
num = Q_SHIFT_RND(p, 52, 23);
den = den * k;
inc = (int32_t)(num / den);
y0 += inc;
}

return y0 + ONE_Q23;
}

/* Decibels to linear conversion: The function uses exp() to calculate
* the linear value. The argument is multiplied by log(10)/20 to
* calculate equivalent of 10^(db/20).
Expand All @@ -68,50 +32,5 @@ int32_t db2lin_fixed(int32_t db)

/* Q8.24 x Q5.27, result needs to be Q5.27 */
arg = (int32_t)Q_MULTSR_32X32((int64_t)db, LOG10_DIV20_Q27, 24, 27, 27);
return exp_fixed(arg);
}

/* Fixed point exponent function for approximate range -11.5 .. 7.6
* that corresponds to decibels range -100 .. +66 dB.
*
* The functions uses rule exp(x) = exp(x/2) * exp(x/2) to reduce
* the input argument for private small value exp() function that is
* accurate with input range -2.0 .. +2.0. The number of possible
* divisions by 2 is computed into variable n. The returned value is
* exp()^(2^n).
*
* Input is Q5.27, -16.0 .. +16.0, but note the input range limitation
* Output is Q12.20, 0.0 .. +2048.0
*/

int32_t exp_fixed(int32_t x)
{
int32_t xs;
int32_t y;
int32_t y0;
int i;
int n = 0;

if (x < Q_CONVERT_FLOAT(-11.5, 27))
return 0;

if (x > Q_CONVERT_FLOAT(7.6245, 27))
return INT32_MAX;

/* x is Q5.27 */
xs = x;
while (xs >= TWO_Q27 || xs <= MINUS_TWO_Q27) {
xs >>= 1;
n++;
}

/* exp_small_fixed() input is Q3.29, while x1 is Q5.27
* exp_small_fixed() output is Q9.23, while y0 is Q12.20
*/
y0 = Q_SHIFT_RND(exp_small_fixed(Q_SHIFT_LEFT(xs, 27, 29)), 23, 20);
y = ONE_Q20;
for (i = 0; i < (1 << n); i++)
y = (int32_t)Q_MULTSR_32X32((int64_t)y, y0, 20, 20, 20);

return y;
return sofm_exp_fixed(arg);
}
54 changes: 53 additions & 1 deletion src/math/exp_fcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*
*/

#include <sof/audio/drc/drc_plat_conf.h>
#include <sof/math/decibels.h>
#include <sof/audio/format.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/numbers.h>
#include <sof/common.h>
Expand All @@ -20,7 +23,7 @@
#define SOFM_BIT_MASK_Q62P2 0x4000000000000000LL
#define SOFM_CONVERG_ERROR 28823037607936LL // error smaller than 1e-4,1/2 ^ -44.7122876200884
#define SOFM_BIT_MASK_LOW_Q27P5 0x8000000
#define SOFM_QUOTIENT_SCALE BIT(30)
#define SOFM_QUOTIENT_SCALE 0x400000000
#define SOFM_TERMS_Q23P9 8388608
#define SOFM_LSHIFT_BITS 8192

Expand Down Expand Up @@ -176,6 +179,7 @@ static inline int64_t lomul_s64_sr_sat_near(int64_t a, int64_t b)
*| 32 | 28 | 1 | 32 | 23 | 0 | 4.28 | 9.23 |
*+------------------+-----------------+--------+--------+
*/
//static inline int32_t sofm_exp_int32(int32_t x)
int32_t sofm_exp_int32(int32_t x)
{
uint64_t ou0Hi;
Expand Down Expand Up @@ -217,4 +221,52 @@ int32_t sofm_exp_int32(int32_t x)
}
return ts;
}

#define ONE_Q20 Q_CONVERT_FLOAT(1.0, 20) /* Use Q12.20 */
#define TWO_Q27 Q_CONVERT_FLOAT(2.0, 27) /* Use Q5.27 */
#define MINUS_TWO_Q27 Q_CONVERT_FLOAT(-2.0, 27) /* Use Q5.27 */

/* Fixed point exponent function for approximate range -11.5 .. 7.6
* that corresponds to decibels range -100 .. +66 dB.
*
* The functions uses rule exp(x) = exp(x/2) * exp(x/2) to reduce
* the input argument for private small value exp() function that is
* accurate with input range -2.0 .. +2.0. The number of possible
* divisions by 2 is computed into variable n. The returned value is
* exp()^(2^n).
*
* Input is Q5.27, -16.0 .. +16.0, but note the input range limitation
* Output is Q12.20, 0.0 .. +2048.0
*/
int32_t sofm_exp_fixed(int32_t x)
{
int32_t xs;
int32_t y;
int32_t y0;
int i;
int n = 0;

if (x < Q_CONVERT_FLOAT(-11.5, 27))
return 0;

if (x > Q_CONVERT_FLOAT(7.6245, 27))
return INT32_MAX;

/* x is Q5.27 */
xs = x;
while (xs >= TWO_Q27 || xs <= MINUS_TWO_Q27) {
xs >>= 1;
n++;
}

/* sofm_exp_int32() input is Q4.28, while x1 is Q5.27
* sofm_exp_int32() output is Q9.23, while y0 is Q12.20
*/
y0 = Q_SHIFT_RND(sofm_exp_int32(Q_SHIFT_LEFT(xs, 27, 28)), 23, 20);
y = ONE_Q20;
for (i = 0; i < (1 << n); i++)
y = (int32_t)Q_MULTSR_32X32((int64_t)y, y0, 20, 20, 20);

return y;
}
#endif
Loading

0 comments on commit 06bc952

Please sign in to comment.