diff --git a/lib_com/options.h b/lib_com/options.h index b2727be5ac198886445ef794e1411432b5daf421..f4e95861aebf6716b6c5e04114fd63323357da61 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -98,6 +98,9 @@ #define FIX_2448_RENDERER_MSAN_ERROR /* FhG: basop issue 2448: fix MSAN error with MSA rendering */ #define FIX_2283_ISM_MD_DELAY /* Dolby: Fix ISM metadata delay round-off */ +#define FIX_2436_CLDFBANAHANDLE_ADRESS /*FhG: cldfb handle pointer were handed over in faulty manner*/ +#define FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR /*FhG: basop issue 2436 (related to basop 2283): fix garbage output for >1 object OMASA with extrend as ISAR prerenderer */ + /* ##################### End NON-BE switches ########################### */ /* ################## End MAINTENANCE switches ######################### */ diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index e121b5929257e8ae2db2fd89e1173426d8ee610e..822f42882961e61edaab5576e35d417f3bb19e01 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -529,6 +529,92 @@ static void accumulate2dArrayToBuffer_fx( return; } + + +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR +/*-------------------------------------------------------------------* + * audio_buffer_td_to_cldfb_fx() + * + * Performs CLDFB analysis on contents of td_buffer and mixes the result into cldfb_buffer. + * + * This function **does not** clear the destination buffer before writing. + * + * The number of valid CLDFB handles in cldfbAna must be a least equal to the number of channels + * in the audio buffers. + *-------------------------------------------------------------------*/ +static void audio_buffer_td_to_cldfb_fx( + IVAS_REND_AudioBuffer td_buffer, + IVAS_REND_AudioBuffer cldfb_buffer, + Word32 fs, + HANDLE_CLDFB_FILTER_BANK *cldfbAna ) +{ + Word16 ch, slot_idx; + Word32 *td_read_ptr; + Word32 *cldfb_write_ptr; + Word16 num_bands, num_slots; + const Word32 inv48000_Q15r = 0x57619F10 /*1/48000 in Q15, rounded*/; + + assert( !td_buffer.config.is_cldfb ); + assert( cldfb_buffer.config.is_cldfb ); + + assert( EQ_16( td_buffer.config.numChannels, cldfb_buffer.config.numChannels ) ); + assert( EQ_16( shl( td_buffer.config.numSamplesPerChannel, 1 ), cldfb_buffer.config.numSamplesPerChannel ) ); + + td_read_ptr = td_buffer.data_fx; + cldfb_write_ptr = cldfb_buffer.data_fx; + num_bands = shl( extract_h( Mpy_32_32( imult3216( fs, CLDFB_NO_CHANNELS_MAX ), inv48000_Q15r ) ), 1 ) /*CLDFB*fs/48000 in Q0*/; + assert( td_buffer.config.numSamplesPerChannel % num_bands == 0 ); + num_slots = idiv1616( td_buffer.config.numSamplesPerChannel, num_bands ); + + FOR( ch = 0; ch < cldfb_buffer.config.numChannels; ++ch ) + { + FOR( slot_idx = 0; slot_idx < num_slots; ++slot_idx ) + { + Word32 re[CLDFB_NO_CHANNELS_MAX]; + Word32 im[CLDFB_NO_CHANNELS_MAX]; + Word16 q_cldfb = *( cldfb_buffer.pq_fact ); + + cldfbAnalysis_ts_fx( td_read_ptr, + re, + im, + num_bands, + cldfbAna[ch], + &q_cldfb ); + + td_read_ptr += num_bands; + + /* scale re and im according to exp-q_cldfb */ + { + Word16 exp = *( cldfb_buffer.pq_fact ); + Word16 noChannels = cldfbAna[ch]->no_channels; + + Word16 scale = sub( exp, q_cldfb ); + FOR( Word16 j = 0; j < noChannels; j++ ) + { + re[j] = L_shl( re[j], scale ); /*Q(exp)*/ + im[j] = L_shl( im[j], scale ); /*Q(exp)*/ + } + } + + FOR( int smplIdx = 0; smplIdx < num_bands; smplIdx++ ) + { + *cldfb_write_ptr = L_add( *cldfb_write_ptr, re[smplIdx] ); + move32(); + cldfb_write_ptr++; + } + + FOR( int smplIdx = 0; smplIdx < num_bands; smplIdx++ ) + { + *cldfb_write_ptr = L_add( *cldfb_write_ptr, im[smplIdx] ); + move32(); + cldfb_write_ptr++; + } + } + } +} +#endif + + /*-------------------------------------------------------------------* * limitRendererOutput() * @@ -7069,16 +7155,25 @@ static ivas_error renderIsmToSplitBinaural( const SPLIT_REND_WRAPPER *pSplitRendWrapper; IVAS_QUATERNION originalHeadRot[MAX_PARAM_SPATIAL_SUBFRAMES]; IVAS_QUATERNION localHeadRot[MAX_PARAM_SPATIAL_SUBFRAMES]; - Word16 i, ch, slot_idx, num_bands; + Word16 i; Word32 tmpBinaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][L_FRAME48k]; +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + Word16 ch, slot_idx, num_bands; Word32 tmpBinaural_CldfbRe[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; Word32 tmpBinaural_CldfbIm[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; +#endif Word16 output_frame = ismInput->base.inputBuffer.config.numSamplesPerChannel; COMBINED_ORIENTATION_HANDLE pCombinedOrientationData; Word16 ism_md_subframe_update_ext, exp; push_wmops( "renderIsmToSplitBinaural" ); +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + assert( !outAudio.config.is_cldfb && + "ISM renderering only supports TD output. If CLDFB output was requested, " + "we convert to CLDFB higher up the stack after rendering all ISMs" ); +#endif + pSplitRendWrapper = ismInput->base.ctx.pSplitRendWrapper; pMultiBinPoseData = &pSplitRendWrapper->multiBinPoseData; @@ -7160,6 +7255,7 @@ static ivas_error renderIsmToSplitBinaural( } } +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR IF( NE_16( outAudio.config.is_cldfb, 0 ) ) { /* Perform CLDFB analysis on rendered audio, since the output buffer is CLDFB domain */ @@ -7175,7 +7271,11 @@ static ivas_error renderIsmToSplitBinaural( &tmpBinaural_CldfbRe[BINAURAL_CHANNELS * pos_idx + ch][slot_idx][0], &tmpBinaural_CldfbIm[BINAURAL_CHANNELS * pos_idx + ch][slot_idx][0], num_bands, +#ifdef FIX_2436_CLDFBANAHANDLE_ADRESS + ismInput->base.ctx.pSplitRendWrapper->hCldfbHandles->cldfbAna[BINAURAL_CHANNELS * pos_idx + ch], +#else ismInput->base.ctx.pSplitRendWrapper->hCldfbHandles->cldfbAna[pos_idx + ch], +#endif &q_cldfb ); /* scale re and im according to exp-q_cldfb */ { @@ -7196,12 +7296,15 @@ static ivas_error renderIsmToSplitBinaural( } ELSE { +#endif /*FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR*/ /* Copy rendered audio to tmp storage buffer. Copying directly to output would * overwrite original audio, which is still needed for rendering next head pose. */ Copy32( tmpProcessing[0], tmpBinaural[i_mult( 2, pos_idx )], output_frame ); Copy32( tmpProcessing[1], tmpBinaural[add( i_mult( 2, pos_idx ), 1 )], output_frame ); +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR } +#endif /* Overwrite processing buffer with original input audio again */ copyBufferTo2dArray_fx( ismInput->base.inputBuffer, tmpProcessing ); @@ -7213,14 +7316,18 @@ static ivas_error renderIsmToSplitBinaural( Copy_Quat_fx( &originalHeadRot[i], &pCombinedOrientationData->Quaternions[i] ); } +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR if ( outAudio.config.is_cldfb ) { accumulateCLDFBArrayToBuffer_fx( tmpBinaural_CldfbRe, tmpBinaural_CldfbIm, exp, &outAudio ); } else { +#endif /*FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR*/ accumulate2dArrayToBuffer_fx( tmpBinaural, &outAudio ); +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR } +#endif pop_wmops(); /* Encoding to split rendering bitstream done at a higher level */ @@ -7373,6 +7480,57 @@ static ivas_error renderActiveInputsIsm( ivas_error error; Word16 input_q = outAudio.q_factor; move16(); + +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + Word16 num_active_inputs; + IVAS_REND_AudioBuffer work_buffer; + Word32 tmp_td_binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS * L_FRAME48k]; + + // Early return in case there are no active ISM inputs + num_active_inputs = 0; + move16(); + pCurrentInput = hIvasRend->inputsIsm; + + FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + if ( NE_32( pCurrentInput->base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) + { + num_active_inputs = add( num_active_inputs, 1 ); + } + ++pCurrentInput; + } + IF( num_active_inputs == 0 ) + { + return IVAS_ERR_OK; + } + + // By default (TD output), use outAudio as work_buffer. This means we render individual ISMs + // directly to outAudio, since the `data` member of both these structs is pointing to the same memory. + // Initializing this way also fixes MSVC's false-positive warning about work_buffer being used + // without initialization in code below, as well as some WMC tool instrumentation problems. + work_buffer = outAudio; + move16(); + move16(); + move16(); + move16(); + move32(); + move32(); + + // ISM rendering only supports TD output. If CLDFB output was requested, we first render + // to TD in a temporary buffer and then convert to CLDFB. We do this by swapping out the + // underlying memory of work_buffer and modifying its config to TD. + IF( outAudio.config.is_cldfb ) + { + work_buffer.config.numSamplesPerChannel = shr( work_buffer.config.numSamplesPerChannel, 1 ); + work_buffer.config.is_cldfb = false; + move16(); + + work_buffer.data_fx = tmp_td_binaural; + move32(); + set32_fx( tmp_td_binaural, 0, MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS * L_FRAME48k ); + } +#endif + FOR( ( i = 0, pCurrentInput = hIvasRend->inputsIsm ); i < RENDERER_MAX_ISM_INPUTS; ( ++i, ++pCurrentInput ) ) { IF( EQ_32( pCurrentInput->base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) @@ -7383,10 +7541,15 @@ static ivas_error renderActiveInputsIsm( *outAudio.pq_fact = input_q; move16(); +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + IF( NE_32( ( error = renderInputIsm( pCurrentInput, hIvasRend->outputConfig, work_buffer ) ), IVAS_ERR_OK ) ) +#else IF( NE_32( ( error = renderInputIsm( pCurrentInput, hIvasRend->outputConfig, outAudio ) ), IVAS_ERR_OK ) ) +#endif { return error; } + FOR( Word16 j = 0; j < outAudio.config.numSamplesPerChannel * outAudio.config.numChannels; ++j ) { outAudio.data_fx[j] = L_shl( outAudio.data_fx[j], sub( sub( input_q, 1 ), ( *outAudio.pq_fact ) ) ); /* Q(input_q - 1) */ @@ -7396,6 +7559,16 @@ static ivas_error renderActiveInputsIsm( move16(); } +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + IF( outAudio.config.is_cldfb ) + { + audio_buffer_td_to_cldfb_fx( work_buffer, + outAudio, + hIvasRend->sampleRateOut, + hIvasRend->splitRendWrapper->hCldfbHandles->cldfbAna ); + } +#endif + return IVAS_ERR_OK; }