Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRC: math: Replace exponential function for performance #8435

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/audio/drc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ config COMP_DRC
bool "Dynamic Range Compressor component"
select CORDIC_FIXED
select NUMBERS_NORM
select MATH_DECIBELS
select MATH_EXP
select COMP_BLOB
default n
help
Expand Down
14 changes: 8 additions & 6 deletions src/audio/drc/drc_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <sof/audio/component.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 @@ -36,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 @@ -66,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 Expand Up @@ -149,7 +151,7 @@ void drc_update_detector_average(struct drc_state *state,
p->sat_release_frames_inv_neg,
21, 30, 24); /* Q8.24 */
sat_release_rate =
db2lin_fixed(db_per_frame) - ONE_Q20; /* Q12.20 */
sofm_db2lin_fixed(db_per_frame) - ONE_Q20; /* Q12.20 */
detector_average += Q_MULTSR_32X32((int64_t)gain_diff,
sat_release_rate, 30, 20, 30);
}
Expand Down Expand Up @@ -226,7 +228,7 @@ void drc_update_envelope(struct drc_state *state, const struct sof_drc_params *p
/* db_per_frame = kSpacingDb / release_frames */
db_per_frame = drc_inv_fixed(release_frames, 12, 30); /* Q2.30 */
db_per_frame = Q_MULTSR_32X32((int64_t)db_per_frame, p->kSpacingDb, 30, 0, 24);
envelope_rate = db2lin_fixed(db_per_frame); /* Q12.20 */
envelope_rate = sofm_db2lin_fixed(db_per_frame); /* Q12.20 */
} else {
int32_t sat32;
/* Attack mode - compression_diff_db should be positive dB */
Expand Down
11 changes: 6 additions & 5 deletions src/audio/drc/drc_hifi3.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <sof/audio/component.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 @@ -42,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 @@ -78,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 Expand Up @@ -159,7 +159,8 @@ void drc_update_detector_average(struct drc_state *state,
db_per_frame = drc_mult_lshift(drc_lin2db_fixed(gain),
p->sat_release_frames_inv_neg,
drc_get_lshift(21, 30, 24));
sat_release_rate = AE_SUB32(db2lin_fixed(db_per_frame), ONE_Q20);
sat_release_rate = AE_SUB32(sofm_db2lin_fixed(db_per_frame),
ONE_Q20);
tmp = drc_mult_lshift(gain_diff, sat_release_rate,
drc_get_lshift(30, 20, 30));
}
Expand Down Expand Up @@ -254,7 +255,7 @@ void drc_update_envelope(struct drc_state *state, const struct sof_drc_params *p
tmp = p->kSpacingDb << 16; /* Q16.16 */
lshift = drc_get_lshift(30, 16, 24);
db_per_frame = drc_mult_lshift(db_per_frame, tmp, lshift); /* Q8.24 */
envelope_rate = db2lin_fixed(db_per_frame); /* Q12.20 */
envelope_rate = sofm_db2lin_fixed(db_per_frame); /* Q12.20 */
} else {
/* Attack mode - compression_diff_db should be positive dB */

Expand Down
11 changes: 6 additions & 5 deletions src/audio/drc/drc_hifi4.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <sof/audio/component.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 @@ -65,7 +65,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, LSHIFT_QX31_QY20_QZ27);
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, LSHIFT_QX24_QY20_QZ24);
knee_curve_k = AE_ADD32(knee_curve_k, p->knee_alpha);
return knee_curve_k;
Expand Down Expand Up @@ -101,7 +101,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, LSHIFT_QX26_QY30_QZ27));
exp_knee = sofm_exp_fixed(drc_mult_lshift(tmp, tmp2, LSHIFT_QX26_QY30_QZ27));
y = drc_mult_lshift(p->ratio_base, exp_knee, LSHIFT_QX30_QY20_QZ30);
}

Expand Down Expand Up @@ -185,7 +185,8 @@ void drc_update_detector_average(struct drc_state *state,
db_per_frame = drc_mult_lshift(drc_lin2db_fixed(gain),
p->sat_release_frames_inv_neg,
LSHIFT_QX21_QY30_QZ24);
sat_release_rate = AE_SUB32(db2lin_fixed(db_per_frame), ONE_Q20);
sat_release_rate = AE_SUB32(sofm_db2lin_fixed(db_per_frame),
ONE_Q20);
tmp = drc_mult_lshift(gain_diff, sat_release_rate,
LSHIFT_QX30_QY20_QZ30);
}
Expand Down Expand Up @@ -278,7 +279,7 @@ void drc_update_envelope(struct drc_state *state, const struct sof_drc_params *p
tmp = p->kSpacingDb << 16; /* Q16.16 */
/* Q8.24 */
db_per_frame = drc_mult_lshift(db_per_frame, tmp, LSHIFT_QX30_QY16_QZ24);
envelope_rate = db2lin_fixed(db_per_frame); /* Q12.20 */
envelope_rate = sofm_db2lin_fixed(db_per_frame); /* Q12.20 */
} else {
/* Attack mode - compression_diff_db should be positive dB */

Expand Down
3 changes: 2 additions & 1 deletion src/audio/drc/drc_math_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#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 @@ -234,7 +235,7 @@ 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));
}

#undef q_multq
Expand Down
40 changes: 37 additions & 3 deletions src/include/sof/math/exp_fcn.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* Author: Shriram Shastry <malladi.sastry@linux.intel.com>
*
*/
#ifndef __SOFM_EXP_H__
#define __SOFM_EXP_H__
#ifndef __SOFM_EXP_FCN_H__
#define __SOFM_EXP_FCN_H__

#include <stdint.h>

Expand All @@ -26,6 +26,40 @@

#endif

int32_t sofm_exp_int32(int32_t x);
/* TODO: Is there a MCPS difference */
#define USING_QCONVERT 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already approved, but let's check in real devices if setting this to zero speeds up the code. In theory it shouldn't impact since Q_CONVERT_FLOAT macro should be evaluated in C pre-processor. Once confirmed we can remove the direct integers. Or if difference seen, remove the Q_CONVERT_FLOAT part.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, can we do this as next steps in a follow up PR and test drive this now.


#if USING_QCONVERT

#include <sof/audio/format.h>
#define SOFM_EXP_FIXED_INPUT_MIN Q_CONVERT_FLOAT(-11.5, 27) /* Q5.27 */
#define SOFM_EXP_FIXED_INPUT_MAX Q_CONVERT_FLOAT(7.6245, 27) /* Q5.27 */
#define SOFM_EXP_TWO_Q27 Q_CONVERT_FLOAT(2.0, 27) /* Q5.27 */
#define SOFM_EXP_MINUS_TWO_Q27 Q_CONVERT_FLOAT(-2.0, 27) /* Q5.27 */
#define SOFM_EXP_ONE_Q20 Q_CONVERT_FLOAT(1.0, 20) /* Q12.20 */
#define SOFM_EXP_MINUS_100_Q24 Q_CONVERT_FLOAT(-100.0, 24) /* Q8.24 */
#define SOFM_EXP_LOG10_DIV20_Q27 Q_CONVERT_FLOAT(0.1151292546, 27) /* Q5.27 */

#else

#define SOFM_EXP_FIXED_INPUT_MIN -1543503872 /* Q_CONVERT_FLOAT(-11.5, 27) */
#define SOFM_EXP_FIXED_INPUT_MAX 1023343067 /* Q_CONVERT_FLOAT(7.6245, 27) */
#define SOFM_EXP_TWO_Q27 268435456 /* Q_CONVERT_FLOAT(2.0, 27) */
#define SOFM_EXP_MINUS_TWO_Q27 -268435456 /* Q_CONVERT_FLOAT(-2.0, 27) */
#define SOFM_EXP_ONE_Q20 1048576 /* Q_CONVERT_FLOAT(1.0, 20) */
#define SOFM_EXP_MINUS_100_Q24 -1677721600 /* Q_CONVERT_FLOAT(-100.0, 24) */
#define SOFM_EXP_LOG10_DIV20_Q27 15452387 /* Q_CONVERT_FLOAT(0.1151292546, 27) */

#endif

#define SOFM_EXP_BIT_MASK_LOW_Q27P5 0x0000000008000000
#define SOFM_EXP_BIT_MASK_Q62P2 0x4000000000000000LL
#define SOFM_EXP_QUOTIENT_SCALE 0x40000000
#define SOFM_EXP_TERMS_Q23P9 0x800000
#define SOFM_EXP_LSHIFT_BITS 0x2000

int32_t sofm_exp_int32(int32_t x);
int32_t sofm_exp_fixed(int32_t x);
int32_t sofm_db2lin_fixed(int32_t db);

#endif /* __SOFM_EXP_FCN_H__ */
90 changes: 78 additions & 12 deletions src/math/exp_fcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*
*/

#include <sof/audio/format.h>
#include <sof/math/exp_fcn.h>
#include <sof/math/numbers.h>
#include <sof/common.h>
Expand All @@ -17,12 +18,7 @@

#if defined(EXPONENTIAL_GENERIC)

#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_TERMS_Q23P9 8388608
#define SOFM_LSHIFT_BITS 8192

/* inv multiplication lookup table */
/* LUT = ceil(1/factorial(b_n) * 2 ^ 63) */
Expand Down Expand Up @@ -155,7 +151,7 @@ static inline int64_t lomul_s64_sr_sat_near(int64_t a, int64_t b)
uint64_t u64_rlo;

mul_s64(a, b, &u64_rhi, &u64_rlo);
const bool roundup = (u64_rlo & SOFM_BIT_MASK_LOW_Q27P5) != 0;
const bool roundup = (u64_rlo & SOFM_EXP_BIT_MASK_LOW_Q27P5) != 0;

u64_rlo = (u64_rhi << 36 | u64_rlo >> 28) + (roundup ? 1 : 0);
return u64_rlo;
Expand All @@ -182,8 +178,8 @@ int32_t sofm_exp_int32(int32_t x)
uint64_t ou0Lo;
int64_t qt;
int32_t b_n;
int32_t ts = SOFM_TERMS_Q23P9; /* Q23.9 */
int64_t dividend = (x + SOFM_LSHIFT_BITS) >> 14; /* x in Q50.14 */
int32_t ts = SOFM_EXP_TERMS_Q23P9; /* Q23.9 */
int64_t dividend = (x + SOFM_EXP_LSHIFT_BITS) >> 14; /* x in Q50.14 */
static const int32_t i_emin = -1342177280; /* Q4.28 */
static const int32_t o_emin = 56601; /* Q9.23 */
static const int32_t i_emax = 1342177280; /* Q4.28 */
Expand All @@ -198,16 +194,16 @@ int32_t sofm_exp_int32(int32_t x)
return o_emax; /* 148.4131494760513306 in Q9.23 */

/* pre-computation of 1st & 2nd terms */
mul_s64(dividend, SOFM_BIT_MASK_Q62P2, &ou0Hi, &ou0Lo);
mul_s64(dividend, SOFM_EXP_BIT_MASK_Q62P2, &ou0Hi, &ou0Lo);
qt = (ou0Hi << 46) | (ou0Lo >> 18);/* Q6.26 */
ts += (int32_t)((qt >> 35) + ((qt & SOFM_QUOTIENT_SCALE) >> 18));
ts += (int32_t)((qt >> 35) + ((qt & SOFM_EXP_QUOTIENT_SCALE) >> 18));
dividend = lomul_s64_sr_sat_near(dividend, x);
for (b_n = 0; b_n < ARRAY_SIZE(exp_iv_ilookup); b_n++) {
mul_s64(dividend, exp_iv_ilookup[b_n], &ou0Hi, &ou0Lo);
qt = (ou0Hi << 45) | (ou0Lo >> 19);

/* sum of the remaining terms */
ts += (int32_t)((qt >> 35) + ((qt & SOFM_QUOTIENT_SCALE) ? 1 : 0));
ts += (int32_t)((qt >> 35) + ((qt & SOFM_EXP_QUOTIENT_SCALE) ? 1 : 0));
dividend = lomul_s64_sr_sat_near(dividend, x);

qt = ABS(qt);
Expand All @@ -217,4 +213,74 @@ int32_t sofm_exp_int32(int32_t x)
}
return ts;
}
#endif

/* 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 < SOFM_EXP_FIXED_INPUT_MIN)
return 0;

if (x > SOFM_EXP_FIXED_INPUT_MAX)
return INT32_MAX;

/* x is Q5.27 */
xs = x;
while (xs >= SOFM_EXP_TWO_Q27 || xs <= SOFM_EXP_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 = SOFM_EXP_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 /* EXPONENTIAL_GENERIC */

/* 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).
*
* The error in conversion is less than 0.1 dB for -89..+66 dB range. Do not
* use the code for argument less than -100 dB. The code simply returns zero
* as linear value for such very small value.
*
* Input is Q8.24 (max 128.0)
* output is Q12.20 (max 2048.0)
*/

int32_t sofm_db2lin_fixed(int32_t db)
{
int32_t arg;

if (db < SOFM_EXP_MINUS_100_Q24)
return 0;

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