diff --git a/lib_com/options.h b/lib_com/options.h index a390324e60ff266f449ec131a31d2c28556fe460..f407476d2bd187e1c5ca21d04622971f8c9ab8e4 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -166,6 +166,7 @@ /* all switches in this category should start with "NONBE_" */ #define NONBE_FIX_1045_ISM_BITRATE_SWITCHING /* Eri: Difference between ROM/File HRTF in ISM bitrate switching */ +#define NONBE_FIX_1067_QUATERNIONSLERP_INACCURACIES /* Philips: issue 1067: QuaternionSlerp inaccuracies in corner cases */ /* ##################### End NON-BE switches ########################### */ diff --git a/lib_rend/ivas_orient_trk.c b/lib_rend/ivas_orient_trk.c index 81cda7dd973d4fe4ea9c824fc50498fe7c32c01a..0e2525adb7dab662df9fb96a63b00ad1caca824b 100644 --- a/lib_rend/ivas_orient_trk.c +++ b/lib_rend/ivas_orient_trk.c @@ -49,7 +49,9 @@ *------------------------------------------------------------------------------------------*/ #define OTR_UPDATE_RATE (float) FRAMES_PER_SEC /* rate of the Process() calls [Hz]; 1x per IVAS frame */ - +#ifdef NONBE_FIX_1067_QUATERNIONSLERP_INACCURACIES +#define COS_ONE_TENTH_DEGREE ( 0.999998476913288f ) +#endif /*------------------------------------------------------------------------------------------* * Local functions @@ -157,12 +159,53 @@ void QuaternionSlerp( const float t, IVAS_QUATERNION *const r ) { +#ifdef NONBE_FIX_1067_QUATERNIONSLERP_INACCURACIES + IVAS_QUATERNION r1, r2; + float phi, sinPhi, cosPhi, s1, s2; + + QuaternionNormalize( q1, &r1 ); + QuaternionNormalize( q2, &r2 ); + + cosPhi = QuaternionDotProduct( r1, r2 ); + + if ( cosPhi < 0 ) + { + cosPhi = -cosPhi; + r2.w = -r2.w; + r2.x = -r2.x; + r2.y = -r2.y; + r2.z = -r2.z; + } + + /* Angle less than one degree, use linear interpolation */ + if ( cosPhi >= COS_ONE_TENTH_DEGREE ) + { + r->w = r1.w + t * ( r2.w - r1.w ); + r->x = r1.x + t * ( r2.x - r1.x ); + r->y = r1.y + t * ( r2.y - r1.y ); + r->z = r1.z + t * ( r2.z - r1.z ); + } + else + { + phi = acosf( cosPhi ); + sinPhi = sinf( phi ); + + s1 = sinf( ( 1 - t ) * phi ); + s2 = sinf( t * phi ); + + r->w = ( s1 * r1.w + s2 * r2.w ) / sinPhi; + r->x = ( s1 * r1.x + s2 * r2.x ) / sinPhi; + r->y = ( s1 * r1.y + s2 * r2.y ) / sinPhi; + r->z = ( s1 * r1.z + s2 * r2.z ) / sinPhi; + } +#else float angle, denom, s, s2; s = QuaternionDotProduct( q1, q2 ); if ( fabsf( s ) >= 1.0f ) { + *r = q2; return; } @@ -177,6 +220,7 @@ void QuaternionSlerp( r->z = ( q1.z * s + q2.z * s2 ) / denom; r->w = ( q1.w * s + q2.w * s2 ) / denom; +#endif QuaternionNormalize( *r, r ); return;