shithub: opus

Download patch

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;
 }
--