diff --git a/lib_com/options.h b/lib_com/options.h index c0cd3fbb39c3b43142a16560c6a5f78e3881be31..d0430c37bd1219151737b8bd19e961b264ac4ac6 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -173,6 +173,7 @@ #define FIX_2432_ISM_SPIKES_16KHZ /* VA: basop issue 2432: fix spikes in ISM decoding at 16kHz output sampling rate */ #define FIX_FLOAT_1518 /* FhG: fix issue 1518: loudness differences in OSBA decoding to mono or stereo output */ +#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 ########################### */ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 3d9609359b4f4421c7275f10c57f97f886e463fe..d31a2cd795548012af4a8a79a548118e1bc4e9ba 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -454,6 +454,66 @@ static void accumulate2dArrayToBuffer( return; } + +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR +/*-------------------------------------------------------------------* + * audio_buffer_td_to_cldfb() + * + * 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( + IVAS_REND_AudioBuffer td_buffer, + IVAS_REND_AudioBuffer cldfb_buffer, + int32_t fs, + HANDLE_CLDFB_FILTER_BANK *cldfbAna ) +{ + int16_t ch, slot_idx; + float *td_read_ptr; + float *cldfb_write_ptr; + int16_t num_bands, num_slots; + + assert( !td_buffer.config.is_cldfb ); + assert( cldfb_buffer.config.is_cldfb ); + + assert( td_buffer.config.numChannels == cldfb_buffer.config.numChannels ); + assert( td_buffer.config.numSamplesPerChannel * 2 == cldfb_buffer.config.numSamplesPerChannel ); + + td_read_ptr = td_buffer.data; + cldfb_write_ptr = cldfb_buffer.data; + num_bands = (int16_t) ( ( CLDFB_NO_CHANNELS_MAX * fs ) / 48000 ); + assert( td_buffer.config.numSamplesPerChannel % num_bands == 0 ); + num_slots = 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 ) + { + float re[CLDFB_NO_CHANNELS_MAX]; + float im[CLDFB_NO_CHANNELS_MAX]; + + cldfbAnalysis_ts( td_read_ptr, + re, + im, + num_bands, + cldfbAna[ch] ); + + td_read_ptr += num_bands; + + v_add( re, cldfb_write_ptr, cldfb_write_ptr, num_bands ); + cldfb_write_ptr += num_bands; + v_add( im, cldfb_write_ptr, cldfb_write_ptr, num_bands ); + cldfb_write_ptr += num_bands; + } + } +} +#endif + + /*-------------------------------------------------------------------* * limitRendererOutput() * @@ -6063,16 +6123,25 @@ static ivas_error renderIsmToSplitBinaural( const MULTI_BIN_REND_POSE_DATA *pMultiBinPoseData; const SPLIT_REND_WRAPPER *pSplitRendWrapper; IVAS_QUATERNION originalHeadRot[MAX_PARAM_SPATIAL_SUBFRAMES]; - int16_t i, ch, slot_idx, num_bands; + int16_t i; float tmpBinaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][L_FRAME48k]; +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + int16_t ch, slot_idx, num_bands; float tmpBinaural_CldfbRe[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; float tmpBinaural_CldfbIm[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; +#endif int16_t output_frame = ismInput->base.inputBuffer.config.numSamplesPerChannel; COMBINED_ORIENTATION_HANDLE pCombinedOrientationData; int16_t ism_md_subframe_update_ext; 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; @@ -6133,6 +6202,7 @@ static ivas_error renderIsmToSplitBinaural( return error; } +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR if ( outAudio.config.is_cldfb ) { /* Perform CLDFB analysis on rendered audio, since the output buffer is CLDFB domain */ @@ -6151,11 +6221,14 @@ static ivas_error renderIsmToSplitBinaural( } else { +#endif /* Copy rendered audio to tmp storage buffer. Copying directly to output would * overwrite original audio, which is still needed for rendering next head pose. */ mvr2r( tmpProcessing[0], tmpBinaural[BINAURAL_CHANNELS * pos_idx], output_frame ); mvr2r( tmpProcessing[1], tmpBinaural[BINAURAL_CHANNELS * pos_idx + 1], output_frame ); +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR } +#endif /* Overwrite processing buffer with original input audio again */ copyBufferTo2dArray( ismInput->base.inputBuffer, tmpProcessing ); @@ -6167,14 +6240,18 @@ static ivas_error renderIsmToSplitBinaural( pCombinedOrientationData->Quaternions[i] = originalHeadRot[i]; } +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR if ( outAudio.config.is_cldfb ) { accumulateCLDFBArrayToBuffer( tmpBinaural_CldfbRe, tmpBinaural_CldfbIm, &outAudio ); } else { +#endif accumulate2dArrayToBuffer( tmpBinaural, &outAudio ); +#ifndef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR } +#endif pop_wmops(); /* Encoding to split rendering bitstream done at a higher level */ @@ -6281,6 +6358,42 @@ static ivas_error renderActiveInputsIsm( int16_t i; input_ism *pCurrentInput; ivas_error error; +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + int16_t num_active_inputs; + IVAS_REND_AudioBuffer work_buffer; + float 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; + for ( i = 0, pCurrentInput = hIvasRend->inputsIsm; i < RENDERER_MAX_ISM_INPUTS; ++i, ++pCurrentInput ) + { + if ( pCurrentInput->base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) + { + ++num_active_inputs; + } + } + 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; + + // 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 /= 2; + work_buffer.config.is_cldfb = false; + work_buffer.data = tmp_td_binaural; + set_zero( tmp_td_binaural, MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS * L_FRAME48k ); + } +#endif for ( i = 0, pCurrentInput = hIvasRend->inputsIsm; i < RENDERER_MAX_ISM_INPUTS; ++i, ++pCurrentInput ) { @@ -6290,12 +6403,28 @@ static ivas_error renderActiveInputsIsm( continue; } +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + if ( ( error = renderInputIsm( pCurrentInput, + hIvasRend->outputConfig, + work_buffer ) ) != IVAS_ERR_OK ) +#else if ( ( error = renderInputIsm( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK ) +#endif { return error; } } +#ifdef FIX_BASOP_2436_REUSED_CLDFB_IN_OMASA_SR + if ( outAudio.config.is_cldfb ) + { + audio_buffer_td_to_cldfb( work_buffer, + outAudio, + hIvasRend->sampleRateOut, + hIvasRend->splitRendWrapper->hCldfbHandles->cldfbAna ); + } +#endif + return IVAS_ERR_OK; }