ref: ea3eb56a01a39415312ad8c6b2021724b144cb34
parent: 255f0130353213d2b58af7b521fb88747ff6c74b
author: Yunho Huh <yunho@google.com>
date: Tue Dec 10 16:54:34 EST 2024
Update the celt_exp2_db to match the new floating point celt_exp2. Change-Id: Ib016fc8f0066980e3e5122b3f286680adc228ff9 Signed-off-by: Jean-Marc Valin <jeanmarcv@google.com>
--- a/celt/bands.c
+++ b/celt/bands.c
@@ -243,7 +243,7 @@
g = celt_exp2_db(MIN32(32.f, lg));
#else
/* Handle the integer part of the log energy */
- shift = 16-(lg>>DB_SHIFT);
+ shift = 15-(lg>>DB_SHIFT);
if (shift>31)
{
shift=0;
--- a/celt/mathops.h
+++ b/celt/mathops.h
@@ -218,7 +218,7 @@
* employing a base-2 exponential function and utilizing a Remez approximation
* of order 5, ensuring a controlled relative error.
* exp2(x) = exp2(integer + fraction)
- * ~ exp2(integer) + exp2(fraction) */
+ * = exp2(integer) * exp2(fraction) */
static OPUS_INLINE float celt_exp2(float x)
{
int integer;
@@ -387,12 +387,30 @@
ADD32(LOG2_COEFF_A0, tmp)));
}
+/* Calculates exp2 for Q28 within a specific range (0 to 1.0) using fixed-point
+ * arithmetic. The input number must be adjusted for Q DB_SHIFT. */
static OPUS_INLINE opus_val32 celt_exp2_db_frac(opus_val32 x)
{
- return (int)floor(.5f + (1<<14)*exp(0.6931471806f*x/(float)(1<<DB_SHIFT))*32768.f);
+ /* Approximation constants. */
+ static const opus_int32 EXP2_COEFF_A0 = 268435440; /* Q28 */
+ static const opus_int32 EXP2_COEFF_A1 = 744267456; /* Q30 */
+ static const opus_int32 EXP2_COEFF_A2 = 1031451904; /* Q32 */
+ static const opus_int32 EXP2_COEFF_A3 = 959088832; /* Q34 */
+ static const opus_int32 EXP2_COEFF_A4 = 617742720; /* Q36 */
+ static const opus_int32 EXP2_COEFF_A5 = 516104352; /* Q38 */
+ opus_int32 tmp;
+ /* Converts input value from Q24 to Q29. */
+ opus_val32 x_q29 = SHL32(x, 29 - 24);
+ /* Split evaluation in steps to avoid exploding macro expansion. */
+ tmp = ADD32(EXP2_COEFF_A4, MULT32_32_Q31(x_q29, EXP2_COEFF_A5));
+ tmp = ADD32(EXP2_COEFF_A3, MULT32_32_Q31(x_q29, tmp));
+ tmp = ADD32(EXP2_COEFF_A2, MULT32_32_Q31(x_q29, tmp));
+ tmp = ADD32(EXP2_COEFF_A1, MULT32_32_Q31(x_q29, tmp));
+ return ADD32(EXP2_COEFF_A0, MULT32_32_Q31(x_q29, tmp));
}
-/** Base-2 exponential approximation (2^x). (DB input, Q16 output) */
+/* Calculates exp2 for Q16 using fixed-point arithmetic. The input number must
+ * be adjusted for Q DB_SHIFT. */
static OPUS_INLINE opus_val32 celt_exp2_db(opus_val32 x)
{
int integer;
@@ -400,15 +418,15 @@
integer = SHR32(x,DB_SHIFT);
if (integer>14)
return 0x7f000000;
- else if (integer <= -14)
+ else if (integer <= -17)
return 0;
- frac = celt_exp2_db_frac(x-SHL32(integer,DB_SHIFT));
- return VSHR32(frac, -integer-2+15);
+ frac = celt_exp2_db_frac(x-SHL32(integer, DB_SHIFT)); /* Q28 */
+ return VSHR32(frac, -integer + 28 - 16); /* Q16 */
}
#else
#define celt_log2_db(x) SHL32(EXTEND32(celt_log2(x)), DB_SHIFT-10)
-#define celt_exp2_db_frac(x) SHL32(celt_exp2_frac(PSHR32(x, DB_SHIFT-10)), 15)
+#define celt_exp2_db_frac(x) SHL32(celt_exp2_frac(PSHR32(x, DB_SHIFT-10)), 14)
#define celt_exp2_db(x) celt_exp2(PSHR32(x, DB_SHIFT-10))
#endif
--- a/celt/tests/test_unit_mathops.c
+++ b/celt/tests/test_unit_mathops.c
@@ -276,6 +276,42 @@
}
}
+void testexp2_db(void)
+{
+#if defined(ENABLE_QEXT)
+ float absolute_error = -1;
+ float absolute_error_threshold = FIX_INT_TO_DOUBLE(2, 16);
+ float relative_error_threshold = -2;
+ float fx;
+ float quantized_fx;
+ opus_val32 x_32;
+
+ for (fx = -32.0; fx < 15.0; fx += 0.0007)
+ {
+ double ground_truth;
+ x_32 = DOUBLE_TO_FIX_INT(fx, DB_SHIFT);
+ quantized_fx = FIX_INT_TO_DOUBLE(x_32, DB_SHIFT);
+
+ ground_truth = (exp(0.6931471805599453094 * quantized_fx));
+ absolute_error = fabs(ground_truth -
+ FIX_INT_TO_DOUBLE(celt_exp2_db(x_32), 16));
+
+ relative_error_threshold = 1.24e-7 * ground_truth;
+ if (absolute_error > absolute_error_threshold &&
+ absolute_error > relative_error_threshold)
+ {
+ fprintf(stderr,
+ "celt_exp2_db failed: "
+ "absolute_error: [%.5e > %.5e] "
+ "relative_error: [%.5e > %.5e] (x = %f)\n",
+ absolute_error, absolute_error_threshold,
+ absolute_error, relative_error_threshold, quantized_fx);
+ ret = 1;
+ }
+ }
+#endif /* defined(ENABLE_QEXT) */
+}
+
void testexp2log2(void)
{
opus_val32 x;
@@ -327,6 +363,7 @@
#ifdef FIXED_POINT
testilog2();
testlog2_db();
+ testexp2_db();
#endif
return ret;
}
--
⑨