Skip to content

Commit 57a17aa

Browse files
yunhohuhgjmvalin
authored andcommitted
Implement fixed-point normalized atan and atan2p functions.
Change-Id: I12463cdafb44e6bf9a66502a464187e7217e839a Signed-off-by: Jean-Marc Valin <[email protected]>
1 parent f76b610 commit 57a17aa

File tree

3 files changed

+148
-5
lines changed

3 files changed

+148
-5
lines changed

celt/mathops.h

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ static OPUS_INLINE opus_val32 celt_maxabs32(const opus_val32 *x, int len)
128128
#endif
129129
#endif
130130

131-
#if !defined(FIXED_POINT) || defined(ENABLE_QEXT)
131+
#ifndef FIXED_POINT
132132
/* Calculates the arctangent of x using a Remez approximation of order 15,
133133
* incorporating only odd-powered terms. */
134134
static OPUS_INLINE float celt_atan_norm(float x)
@@ -176,7 +176,9 @@ static OPUS_INLINE float celt_atan2p_norm(float y, float x)
176176
return 1.f - celt_atan_norm(x / y);
177177
}
178178
}
179+
#endif
179180

181+
#if !defined(FIXED_POINT) || defined(ENABLE_QEXT)
180182
/* Computes estimated cosine values for (PI/2 * x) using only terms with even
181183
* exponents. */
182184
static OPUS_INLINE float celt_cos_norm2(float x)
@@ -521,6 +523,68 @@ opus_val32 celt_rcp(opus_val32 x);
521523
opus_val32 frac_div32_q29(opus_val32 a, opus_val32 b);
522524
opus_val32 frac_div32(opus_val32 a, opus_val32 b);
523525

526+
/* Computes atan(x) multiplied by 2/PI. The input value (x) should be within the
527+
* range of -1 to 1 and represented in Q30 format. The function will return the
528+
* result in Q30 format. */
529+
static OPUS_INLINE opus_val32 celt_atan_norm(opus_val32 x)
530+
{
531+
/* Approximation constants. */
532+
static const opus_int32 ATAN_2_OVER_PI = 1367130551; /* Q31 */
533+
static const opus_int32 ATAN_COEFF_A03 = -715791936; /* Q31 */
534+
static const opus_int32 ATAN_COEFF_A05 = 857391616; /* Q32 */
535+
static const opus_int32 ATAN_COEFF_A07 = -1200579328; /* Q33 */
536+
static const opus_int32 ATAN_COEFF_A09 = 1682636672; /* Q34 */
537+
static const opus_int32 ATAN_COEFF_A11 = -1985085440; /* Q35 */
538+
static const opus_int32 ATAN_COEFF_A13 = 1583306112; /* Q36 */
539+
static const opus_int32 ATAN_COEFF_A15 = -598602432; /* Q37 */
540+
opus_int32 x_sq_q30;
541+
opus_int32 x_q31;
542+
opus_int32 tmp;
543+
/* The expected x is in the range of [-1.0f, 1.0f] */
544+
celt_sig_assert((x <= 1073741824) && (x >= -1073741824));
545+
546+
/* If x = 1.0f, returns 0.5f */
547+
if (x == 1073741824)
548+
{
549+
return 536870912; /* 0.5f (Q30) */
550+
}
551+
/* If x = 1.0f, returns 0.5f */
552+
if (x == -1073741824)
553+
{
554+
return -536870912; /* -0.5f (Q30) */
555+
}
556+
x_q31 = SHL32(x, 1);
557+
x_sq_q30 = MULT32_32_Q31(x_q31, x);
558+
/* Split evaluation in steps to avoid exploding macro expansion. */
559+
tmp = MULT32_32_Q31(x_sq_q30, ATAN_COEFF_A15);
560+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A13, tmp));
561+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A11, tmp));
562+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A09, tmp));
563+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A07, tmp));
564+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A05, tmp));
565+
tmp = MULT32_32_Q31(x_sq_q30, ADD32(ATAN_COEFF_A03, tmp));
566+
tmp = ADD32(x, MULT32_32_Q31(x_q31, tmp));
567+
return MULT32_32_Q31(ATAN_2_OVER_PI, tmp);
568+
}
569+
570+
/* Calculates the arctangent of y/x, multiplies the result by 2/pi, and returns
571+
* the value in Q30 format. Both input values (x and y) must be within the range
572+
* of 0 to 1 and represented in Q30 format. Inputs must be zero or greater, and
573+
* at least one input must be non-zero. */
574+
static OPUS_INLINE opus_val32 celt_atan2p_norm(opus_val32 y, opus_val32 x)
575+
{
576+
celt_sig_assert(x>=0 && y>=0);
577+
if (y==0 && x==0) {
578+
return 0;
579+
} else if (y < x) {
580+
return celt_atan_norm(SHR32(frac_div32(y, x), 1));
581+
} else {
582+
celt_sig_assert(y > 0);
583+
return 1073741824 /* 1.0f Q30 */ -
584+
celt_atan_norm(SHR32(frac_div32(x, y), 1));
585+
}
586+
}
587+
524588
#define M1 32767
525589
#define M2 -21
526590
#define M3 -11943

celt/tests/test_unit_mathops.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,86 @@ void testrsqrt(void)
439439
}
440440
fprintf (stdout, "celt_rsqrt_norm32 max_error: %.7e\n", max_error);
441441
}
442+
443+
void testatan_norm(void)
444+
{
445+
#if defined(ENABLE_QEXT)
446+
float error = -1;
447+
float max_error = -2;
448+
float error_threshold = 5.97e-08;
449+
float fx = 0;
450+
opus_int32 x = 0;
451+
int q_input = 30;
452+
int q_output = 30;
453+
#define ATAN2_2_OVER_PI 0.636619772367581f
454+
for (fx = -1.0f; fx <= 1.0f; fx += 0.007f)
455+
{
456+
x = DOUBLE_TO_FIX_INT(fx, q_input);
457+
error = fabs(atan(FIX_INT_TO_DOUBLE(x, q_input)) * ATAN2_2_OVER_PI -
458+
FIX_INT_TO_DOUBLE(celt_atan_norm(x), q_output));
459+
if (error > max_error)
460+
{
461+
max_error = error;
462+
}
463+
if (error > error_threshold)
464+
{
465+
fprintf(stderr,
466+
"celt_atan_norm failed: error: [%.5e > %.5e] (x = %f)\n",
467+
error, error_threshold, FIX_INT_TO_DOUBLE(x, DB_SHIFT));
468+
ret = 1;
469+
}
470+
}
471+
fprintf(stdout, "celt_atan_norm max_error: %.7e\n", max_error);
472+
#endif /* defined(ENABLE_QEXT) */
473+
}
474+
475+
void testatan2p_norm(void)
476+
{
477+
#if defined(ENABLE_QEXT)
478+
float error = -1;
479+
float max_error = -2;
480+
float error_threshold = 1.2e-07;
481+
float fx = 0;
482+
float fy = 0;
483+
opus_int32 x = 0;
484+
opus_int32 y = 0;
485+
int q_input = 30;
486+
int q_output = 30;
487+
#define ATAN2_2_OVER_PI 0.636619772367581f
488+
for (fx = 0.0f; fx <= 1.0f; fx += 0.007f)
489+
{
490+
x = DOUBLE_TO_FIX_INT(fx, q_input);
491+
for (fy = 0.0f; fy <= 1.0f; fy += 0.007f)
492+
{
493+
y = DOUBLE_TO_FIX_INT(fy, q_input);
494+
if (x == 0 && x == 0)
495+
continue;
496+
497+
error = fabs(atan2(FIX_INT_TO_DOUBLE(y, q_input),
498+
FIX_INT_TO_DOUBLE(x, q_input)) * ATAN2_2_OVER_PI -
499+
FIX_INT_TO_DOUBLE(celt_atan2p_norm(y, x), q_output));
500+
if (error > max_error)
501+
{
502+
max_error = error;
503+
}
504+
if (error > error_threshold)
505+
{
506+
fprintf(stderr,
507+
"celt_atan2p_norm failed: error: [%.5e > %.5e] (x = %f)\n",
508+
error, error_threshold, FIX_INT_TO_DOUBLE(x, DB_SHIFT));
509+
ret = 1;
510+
}
511+
}
512+
}
513+
fprintf(stdout, "celt_atan2p_norm max_error: %.7e\n", max_error);
514+
#endif /* defined(ENABLE_QEXT) */
515+
}
516+
517+
void testatan(void)
518+
{
519+
testatan_norm();
520+
testatan2p_norm();
521+
}
442522
#endif
443523

444524
int main(void)
@@ -455,6 +535,7 @@ int main(void)
455535
testlog2_db();
456536
testexp2_db();
457537
testrsqrt();
538+
testatan();
458539
#else
459540
test_cos();
460541
test_atan2();

celt/vq.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -693,10 +693,8 @@ opus_int32 stereo_itheta(const celt_norm *X, const celt_norm *Y, int stereo, int
693693
}
694694
mid = celt_sqrt(Emid);
695695
side = celt_sqrt(Eside);
696-
/* FIXME: Add a fixed-point version for ENABLE_QEXT*/
697-
#if defined(FIXED_POINT) && !defined(ENABLE_QEXT)
698-
/* 0.63662 = 2/pi */
699-
itheta = MULT16_16(QCONST16(0.63662f,15),celt_atan2p(side, mid))<<1;
696+
#if defined(FIXED_POINT)
697+
itheta = celt_atan2p_norm(side, mid);
700698
#else
701699
itheta = (int)floor(.5f+65536.f*16384*celt_atan2p_norm(side,mid));
702700
#endif

0 commit comments

Comments
 (0)