diff --git a/lib_com/options.h b/lib_com/options.h index c9c5644f935a5d8d62b0e71408d59ef013f4704c..58dfb3295b45287286f37701e81ea2291f47b56c 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -188,6 +188,7 @@ /*#define NONBE_1324_TC_BUFFER_MEMOERY_KEEP*/ /* VA: issue 1324: do not reset TSM memory in JBM bitrate switching */ #define FIX_1370_EXTERNAL_ORIENTATION_CHECK /* Nokia: add sanity check for Euler angles for external orientations */ #define FIX_1413_IGF_INIT_PRINTOUT /* FhG: use correct variable for IGF initiliazation */ +#define NONBE_2169_BINAURAL_MIXING_MATRIX_OPT /* Dlb: use dedicated formulate2x2MixingMatrix() function if cross terms are zero */ #define CODE_IMPROVEMENTS #define NONBE_1359_FIX_IVASREND_OMASA_BINAURAL_LOUDNESS /* Nokia: issue 1339: Apply scaling to the object-part of OMASA for binaural rendering in IVAS_rend. */ #define NONBE_1362_FIX_OMASA_TO_MASA1_RENDERING /* Nokia: Fix OMASA to MASA1 rendering in IVAS_rend */ diff --git a/lib_rend/ivas_dirac_dec_binaural_functions.c b/lib_rend/ivas_dirac_dec_binaural_functions.c index 11a3a16aae1a74300bbcd9126a9b3d761dc2df18..82a1c3baf6fb95c795221447cae3bd00eab68659 100644 --- a/lib_rend/ivas_dirac_dec_binaural_functions.c +++ b/lib_rend/ivas_dirac_dec_binaural_functions.c @@ -111,7 +111,9 @@ static void adaptTransportSignalsHeadtracked( COMBINED_ORIENTATION_HANDLE hHeadT static void ivas_dirac_dec_binaural_check_and_switch_transports_headtracked( COMBINED_ORIENTATION_HANDLE hHeadTrackData, float inRe[][CLDFB_SLOTS_PER_SUBFRAME][CLDFB_NO_CHANNELS_MAX], float inIm[][CLDFB_SLOTS_PER_SUBFRAME][CLDFB_NO_CHANNELS_MAX], const int16_t nBins, const int16_t nSlots, float Rmat[3][3] ); static void formulate2x2MixingMatrix( float Ein1, float Ein2, float CinRe, float CinIm, float Eout1, float Eout2, float CoutRe, float CoutIm, float Q[BINAURAL_CHANNELS][BINAURAL_CHANNELS], float Mre[BINAURAL_CHANNELS][BINAURAL_CHANNELS], float Mim[BINAURAL_CHANNELS][BINAURAL_CHANNELS], const float regularizationFactor ); - +#ifdef NONBE_2169_BINAURAL_MIXING_MATRIX_OPT +static void formulate2x2MixingMatrixNoCross( float Ein1, float Ein2, float Eout1, float Eout2, float CoutRe, float CoutIm, float Q[BINAURAL_CHANNELS][BINAURAL_CHANNELS], float Mre[BINAURAL_CHANNELS][BINAURAL_CHANNELS], float Mim[BINAURAL_CHANNELS][BINAURAL_CHANNELS] ); +#endif static void hrtfShGetHrtf( const int16_t bin, const int16_t aziDeg, const int16_t eleDeg, float *lRealp, float *lImagp, float *rRealp, float *rImagp, PARAMBIN_HRTF_GAIN_CACHE *gainCache, const int16_t useCachedValue, HRTFS_PARAMBIN_HANDLE hHrtfParambin ); static void getDirectPartGains( const int16_t bin, int16_t aziDeg, int16_t eleDeg, float *lRealp, float *lImagp, float *rRealp, float *rImagp, const uint8_t stereoMode, float Rmat[3][3], PARAMBIN_HRTF_GAIN_CACHE *gainCache, const int16_t isHeadtracked, HRTFS_PARAMBIN_HANDLE hHrtfParambin ); @@ -1589,7 +1591,7 @@ static void ivas_dirac_dec_binaural_determine_processing_matrices( CrEneR *= decorrelationReductionFactor; CrCrossRe *= decorrelationReductionFactor; CrCrossIm *= decorrelationReductionFactor; - +#ifndef NONBE_2169_BINAURAL_MIXING_MATRIX_OPT /* Determine a residual mixing matrix Mdec for processing the decorrelated signal to obtain * the residual signal (that has the residual covariance matrix) */ formulate2x2MixingMatrix( hDiracDecBin->ChEne[0][bin], hDiracDecBin->ChEne[1][bin], @@ -1597,6 +1599,15 @@ static void ivas_dirac_dec_binaural_determine_processing_matrices( CrEneL, CrEneR, CrCrossRe, CrCrossIm, prototypeMtx, MdecRe, MdecIm, 0.2f ); +#else + /* Determine a residual mixing matrix Mdec for processing the decorrelated signal to obtain + * the residual signal (that has the residual covariance matrix) + * Decorrelated signal has ideally no cross-terms */ + formulate2x2MixingMatrixNoCross( hDiracDecBin->ChEne[0][bin], hDiracDecBin->ChEne[1][bin], + CrEneL, CrEneR, + CrCrossRe, CrCrossIm, + prototypeMtx, MdecRe, MdecIm ); +#endif } else { @@ -2442,6 +2453,107 @@ static void formulate2x2MixingMatrix( return; } +#ifdef NONBE_2169_BINAURAL_MIXING_MATRIX_OPT +static void formulate2x2MixingMatrixNoCross( + float Ein1, + float Ein2, + float Eout1, + float Eout2, + float CoutRe, + float CoutIm, + float Q[BINAURAL_CHANNELS][BINAURAL_CHANNELS], + float Mre[BINAURAL_CHANNELS][BINAURAL_CHANNELS], + float Mim[BINAURAL_CHANNELS][BINAURAL_CHANNELS] ) +{ + /* + This function implements a 2x2 solution for an optimized spatial audio rendering algorithm + Vilkamo, J., Bäckström, T. and Kuntz, A., 2013. + "Optimized covariance domain framework for time–frequency processing of spatial audio." + Journal of the Audio Engineering Society, 61(6), pp.403-411. + + The result of the formulas below are the same as those in the publication, however, some + derivation details differ for as simple as possible 2x2 formulattion + */ + int16_t chA, chB; + float maxEne, maxEneDiv; + float KyRe[BINAURAL_CHANNELS][BINAURAL_CHANNELS], KyIm[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + float tmpRe[BINAURAL_CHANNELS][BINAURAL_CHANNELS], tmpIm[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + float Are[BINAURAL_CHANNELS][BINAURAL_CHANNELS], Aim[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + float Ure[BINAURAL_CHANNELS][BINAURAL_CHANNELS], Uim[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + float D[BINAURAL_CHANNELS]; + float div[BINAURAL_CHANNELS]; + float Ghat[BINAURAL_CHANNELS]; + float GhatQ[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + float Pre[BINAURAL_CHANNELS][BINAURAL_CHANNELS], Pim[BINAURAL_CHANNELS][BINAURAL_CHANNELS]; + + /* Normalize energy values */ + maxEne = Ein1; + maxEne = max( maxEne, Ein2 ); + maxEne = max( maxEne, Eout1 ); + maxEne = max( maxEne, Eout2 ); + maxEneDiv = 1.0f / ( maxEne + 1e-12f ); + Ein1 *= maxEneDiv; + Ein2 *= maxEneDiv; + Eout1 *= maxEneDiv; + Eout2 *= maxEneDiv; + CoutRe *= maxEneDiv; + CoutIm *= maxEneDiv; + + /* Cholesky decomposition of target / output covariance matrix */ + chol2x2( Eout1, Eout2, CoutRe, CoutIm, KyRe, KyIm ); + + /* If there are no cross-terms, the Eigendecomposition of input covariance matrix + can be skipped. Uxre is a unit matrix, Uxim is a zero matrix and Sx is (1, 1) + Further on, also Kxre is a unit matrix and Kxim is a zero matrix + Multiplication with these matrices / scalars can be skipped + */ + + /* This is equivalent to the prototype signal energy normalization in the publication */ + Ghat[0] = sqrtf( Eout1 / ( 1e-12f + max( Ein1, 0.001f * Ein2 ) ) ); + Ghat[1] = sqrtf( Eout2 / ( 1e-12f + max( Ein2, 0.001f * Ein1 ) ) ); + + /* Matrix multiplication, A = Ky' * G_hat * Q */ + for ( chA = 0; chA < BINAURAL_CHANNELS; chA++ ) + { + GhatQ[chA][0] = Q[chA][0] * Ghat[chA]; + GhatQ[chA][1] = Q[chA][1] * Ghat[chA]; + } + for ( chA = 0; chA < BINAURAL_CHANNELS; chA++ ) + { + for ( chB = 0; chB < BINAURAL_CHANNELS; chB++ ) + { + Are[chA][chB] = KyRe[0][chA] * GhatQ[0][chB] + KyRe[1][chA] * GhatQ[1][chB]; + Aim[chA][chB] = -KyIm[0][chA] * GhatQ[0][chB] - KyIm[1][chA] * GhatQ[1][chB]; + } + } + + /* Find nearest orthonormal matrix P to A = Ky' * G_hat * Q * Kx + For matrix A that is P = A(A'A)^0.5 + */ + matrixTransp1Mul( Are, Aim, Are, Aim, tmpRe, tmpIm ); + + eig2x2( tmpRe[0][0], tmpRe[1][1], tmpRe[1][0], tmpIm[1][0], Ure, Uim, D ); + + div[0] = min( 10000.0f, sqrtf( 1.0f / ( 1e-12f + D[0] ) ) ); + div[1] = min( 10000.0f, sqrtf( 1.0f / ( 1e-12f + D[1] ) ) ); + + matrixMul( Are, Aim, Ure, Uim, tmpRe, tmpIm ); + + for ( chA = 0; chA < BINAURAL_CHANNELS; chA++ ) + { + for ( chB = 0; chB < BINAURAL_CHANNELS; chB++ ) + { + tmpRe[chA][chB] *= div[chB]; + tmpIm[chA][chB] *= div[chB]; + } + } + + matrixTransp2Mul( tmpRe, tmpIm, Ure, Uim, Pre, Pim ); /* Nearest orthonormal matrix P to matrix tmp formulated */ + matrixMul( KyRe, KyIm, Pre, Pim, Mre, Mim ); + + return; +} +#endif static void getDirectPartGains( const int16_t bin,