From 48681f8c15df4a8de94c2aaf188f9b229ed87021 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 16 Oct 2025 15:04:54 +0200 Subject: [PATCH 01/36] port 2171 from float repo to BASOP main Resolve "Delay alignment in external renderer" and "External renderer crashes when rendering OMASA" --- Workspace_msvc/lib_rend.vcxproj | 3 +- apps/renderer.c | 114 ++-- lib_isar/lib_isar_post_rend.c | 6 +- lib_isar/lib_isar_pre_rend.c | 5 +- lib_rend/ivas_prot_rend_fx.h | 35 + lib_rend/ivas_stat_rend.h | 16 + lib_rend/lib_rend.h | 9 +- lib_rend/lib_rend_fx.c | 1128 ++++++++++++++++++++----------- 8 files changed, 841 insertions(+), 475 deletions(-) diff --git a/Workspace_msvc/lib_rend.vcxproj b/Workspace_msvc/lib_rend.vcxproj index c39806d47..5f78402ec 100644 --- a/Workspace_msvc/lib_rend.vcxproj +++ b/Workspace_msvc/lib_rend.vcxproj @@ -175,6 +175,7 @@ + @@ -209,4 +210,4 @@ - \ No newline at end of file + diff --git a/apps/renderer.c b/apps/renderer.c index adf664f97..2d7ba3c8d 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -31,6 +31,7 @@ *******************************************************************************************************/ #include "lib_rend.h" +#include "typedef.h" #include #include #include @@ -797,6 +798,7 @@ int main( SplitFileReadWrite *hSplitRendFileReadWrite; int16_t delayNumSamples_temp; int32_t delayTimeScale_temp; + bool flushRendererLastFrame = false; int16_t numSamplesRead; int16_t delayNumSamples = -1; int16_t delayNumSamples_orig = 0; @@ -842,8 +844,11 @@ int main( CmdlnArgs args = parseCmdlnArgs( argc, argv ); - if ( args.nonDiegeticPan && !( ( args.inConfig.numAudioObjects == 0 && args.inConfig.multiChannelBuses[0].audioConfig == IVAS_AUDIO_CONFIG_MONO ) || - ( args.inConfig.numAudioObjects > 0 && args.inConfig.audioObjects[0].audioConfig == IVAS_AUDIO_CONFIG_OBA && args.inConfig.numAudioObjects == 1 ) ) ) + if ( args.nonDiegeticPan && + !( ( args.inConfig.numAudioObjects == 0 && + args.inConfig.multiChannelBuses[0].audioConfig == IVAS_AUDIO_CONFIG_MONO ) || + ( args.inConfig.numAudioObjects > 0 && + args.inConfig.audioObjects[0].audioConfig == IVAS_AUDIO_CONFIG_OBA && args.inConfig.numAudioObjects == 1 ) ) ) { fprintf( stderr, "\ninvalid configuration - non-diegetic panning requires mono or ISM1 input\n" ); goto cleanup; @@ -1173,7 +1178,10 @@ int main( #endif /* sanity check */ - if ( ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL ) && ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL_ROOM_IR ) && ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) && !is_split_pre_rend_mode( &args ) ) + if ( ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL ) && + ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL_ROOM_IR ) && + ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) && + !is_split_pre_rend_mode( &args ) ) { fprintf( stderr, "\nExternal Renderer Config is supported only when binaural output configurations is used as output OR when Split pre-rendering mode is enabled. Exiting. \n" ); goto cleanup; @@ -1490,7 +1498,7 @@ int main( } Word16 numOutChannels; - if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_GetNumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_REND_NumOutChannels(): %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -1505,8 +1513,16 @@ int main( } } - if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) + if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { + char *outFile = args.outMetadataFilePath; + + if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) + { + outFile = args.outputFilePath; + audioWriter = NULL; + } + if ( ( error = IVAS_REND_GetSplitRendBitstreamHeader( hIvasRend, &bitsBuffer.config.codec, &bitsBuffer.config.poseCorrection, &bitsBuffer.config.codec_frame_size_ms, &bitsBuffer.config.isar_frame_size_ms ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_REND_GetSplitRendBitstreamHeader(): %s!\n", ivas_error_to_string( error ) ); @@ -1519,36 +1535,15 @@ int main( goto cleanup; } - if ( ( error = split_rend_writer_open( &hSplitRendFileReadWrite, args.outputFilePath, delayNumSamples_temp, delayTimeScale_temp, bitsBuffer.config.codec, bitsBuffer.config.poseCorrection, bitsBuffer.config.codec_frame_size_ms, bitsBuffer.config.isar_frame_size_ms, args.sampleRate, bitsBuffer.config.lc3plus_highres ) ) != IVAS_ERR_OK ) + if ( ( error = split_rend_writer_open( &hSplitRendFileReadWrite, outFile, delayNumSamples_temp, delayTimeScale_temp, bitsBuffer.config.codec, bitsBuffer.config.poseCorrection, bitsBuffer.config.codec_frame_size_ms, bitsBuffer.config.isar_frame_size_ms, args.sampleRate, bitsBuffer.config.lc3plus_highres ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "\nCould not open split rend metadata file %s\n", args.outputFilePath ); + fprintf( stderr, "\nCould not open split rend metadata file %s\n", outFile ); goto cleanup; } - audioWriter = NULL; } - else - { - if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) - { - if ( ( error = IVAS_REND_GetSplitRendBitstreamHeader( hIvasRend, &bitsBuffer.config.codec, &bitsBuffer.config.poseCorrection, &bitsBuffer.config.codec_frame_size_ms, &bitsBuffer.config.isar_frame_size_ms ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError in IVAS_REND_GetSplitRendBitstreamHeader(): %s!\n", ivas_error_to_string( error ) ); - goto cleanup; - } - - if ( IVAS_REND_GetDelay_fx( hIvasRend, &delayNumSamples_temp, &delayTimeScale_temp ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nUnable to get delay of renderer!\n" ); - goto cleanup; - } - - if ( ( error = split_rend_writer_open( &hSplitRendFileReadWrite, args.outMetadataFilePath, delayNumSamples_temp, delayTimeScale_temp, bitsBuffer.config.codec, bitsBuffer.config.poseCorrection, bitsBuffer.config.codec_frame_size_ms, bitsBuffer.config.isar_frame_size_ms, args.sampleRate, bitsBuffer.config.lc3plus_highres ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nCould not open split rend metadata file %s\n", args.outMetadataFilePath ); - goto cleanup; - } - } + if ( args.outConfig.audioConfig != IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) + { if ( AudioFileWriter_open( &audioWriter, args.outputFilePath, args.sampleRate, numOutChannels ) != IVAS_ERR_OK ) { fprintf( stderr, "\nFailed to open file: %s\n", args.outputFilePath ); @@ -1693,19 +1688,26 @@ int main( if ( numSamplesRead == 0 ) { /* end of input data */ - break; + flushRendererLastFrame = true; } /* Convert from int to float and from interleaved to packed */ - Word16 Q_out; - *outBuffer.pq_fact = 16 - ( gd_bits ); - convertInputBuffer_fx( inpInt16Buffer, numSamplesRead, inBuffer.config.numSamplesPerChannel, num_in_channels, inInt32Buffer, *outBuffer.pq_fact, inBuffer.config.is_cldfb, cldfbAna, &Q_out ); - *outBuffer.pq_fact = Q_out; + if ( !flushRendererLastFrame ) + { + Word16 Q_out; + *outBuffer.pq_fact = 16 - ( gd_bits ); + convertInputBuffer_fx( inpInt16Buffer, numSamplesRead, inBuffer.config.numSamplesPerChannel, num_in_channels, inInt32Buffer, *outBuffer.pq_fact, inBuffer.config.is_cldfb, cldfbAna, &Q_out ); + *outBuffer.pq_fact = Q_out; + } + else + { + memset( inBuffer.data_fx, 0, inBuffer.config.numChannels * inBuffer.config.numSamplesPerChannel * sizeof( Word32 ) ); + } int16_t num_subframes, sf_idx; num_subframes = (int16_t) args.render_framesize; - if ( isCurrentFrameMultipleOf20ms ) + if ( isCurrentFrameMultipleOf20ms && !flushRendererLastFrame ) { IsmPositionProvider_getNextFrame( positionProvider, &mtdBuffer ); @@ -1817,7 +1819,7 @@ int main( } IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.multiChannelBuses[i].inputChannelIndex, numChannels ); - if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, mcIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, mcIds[i], tmpBuffer, flushRendererLastFrame ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_REND_FeedInputAudio failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -1831,7 +1833,7 @@ int main( if ( i == 0 ) { IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.audioObjects[i].inputChannelIndex, args.inConfig.numAudioObjects ); - if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, ismIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, ismIds[i], tmpBuffer, flushRendererLastFrame ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_REND_FeedInputAudio failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -1847,7 +1849,7 @@ int main( else { IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.audioObjects[i].inputChannelIndex, 1 ); - if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, ismIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, ismIds[i], tmpBuffer, flushRendererLastFrame ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_REND_FeedInputAudio failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -1870,7 +1872,7 @@ int main( } IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.ambisonicsBuses[i].inputChannelIndex, numChannels ); - if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, sbaIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, sbaIds[i], tmpBuffer, flushRendererLastFrame ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_REND_FeedInputAudio failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -1887,13 +1889,13 @@ int main( IVAS_REND_ReadOnlyAudioBuffer tmpBuffer = getReadOnlySubBuffer( inBuffer, (int16_t) args.inConfig.masaBuses[i].inputChannelIndex, numChannels ); - IF( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, masaIds[i], tmpBuffer ) ) != IVAS_ERR_OK ) + IF( ( error = IVAS_REND_FeedInputAudio_fx( hIvasRend, masaIds[i], tmpBuffer, flushRendererLastFrame ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_REND_FeedInputAudio failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; } - if ( isCurrentFrameMultipleOf20ms ) + if ( isCurrentFrameMultipleOf20ms && !flushRendererLastFrame ) { if ( masaReaders[i] != NULL ) { @@ -1956,7 +1958,7 @@ int main( zeroPad = delayNumSamples; } - if ( is_split_pre_rend_mode( &args ) ) + if ( is_split_pre_rend_mode( &args ) && !flushRendererLastFrame ) { if ( split_rend_write_bitstream_to_file( hSplitRendFileReadWrite, bitsBuffer.bits, &bitsBuffer.config.bitsRead, &bitsBuffer.config.bitsWritten ) != IVAS_ERR_OK ) @@ -1966,7 +1968,7 @@ int main( } } - if ( audioWriter != NULL ) + if ( audioWriter != NULL && !flushRendererLastFrame ) { if ( delayNumSamples * num_out_channels < outBufferSize ) { @@ -1987,7 +1989,7 @@ int main( bitsBuffer.config.bitsWritten = 0; /* Write MASA metadata for MASA outputs */ - if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_MASA1 || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_MASA2 ) + if ( !flushRendererLastFrame && ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_MASA1 || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_MASA2 ) ) { IVAS_REND_AudioConfigType inputType1; IVAS_REND_AudioConfigType inputType2; @@ -2059,7 +2061,8 @@ int main( } } - if ( ( args.inConfig.numAmbisonicsBuses > 0 || args.inConfig.numMultiChannelBuses > 0 || args.inConfig.numMasaBuses > 0 ) && args.inConfig.numAudioObjects > 0 ) + if ( ( args.inConfig.numAmbisonicsBuses > 0 || args.inConfig.numMultiChannelBuses > 0 || args.inConfig.numMasaBuses > 0 ) && + args.inConfig.numAudioObjects > 0 ) { inputType2 = IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED; if ( ( error = IVAS_REND_MergeMasaMetadata( hIvasRend, &hMetaOutput, inputType1, inputType2 ) ) != IVAS_ERR_OK ) @@ -2075,6 +2078,13 @@ int main( } } + /* no new input was actually read, only delay buffers were flushed + * therefore this is not a real frame */ + if ( flushRendererLastFrame ) + { + break; + } + frame++; if ( !args.quietModeEnabled ) { @@ -2087,12 +2097,13 @@ int main( #endif } - /* add zeros at the end to have equal length of synthesized signals */ + /* add zeros at the end to have equal length of synthesized signals + * the output buffer will contain either leftover input samples from delay aligned inputs + * or zeros for padding */ if ( audioWriter != NULL ) { for ( zeroPadToWrite = zeroPad; zeroPadToWrite > frameSize_smpls; zeroPadToWrite -= frameSize_smpls ) { - memset( outInt16Buffer, 0, outBufferSize * sizeof( int16_t ) ); if ( ( error = AudioFileWriter_write( audioWriter, outInt16Buffer, outBufferSize ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nOutput audio file writer error\n" ); @@ -2100,7 +2111,6 @@ int main( } } - memset( outInt16Buffer, 0, zeroPadToWrite * outBuffer.config.numChannels * sizeof( int16_t ) ); if ( ( error = AudioFileWriter_write( audioWriter, outInt16Buffer, zeroPadToWrite * outBuffer.config.numChannels ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nOutput audio file writer error\n" ); @@ -2110,7 +2120,8 @@ int main( } - if ( args.inConfig.numAudioObjects != 0 && ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) ) + if ( args.inConfig.numAudioObjects != 0 && + ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) ) { fprintf( stdout, "\n\nMetadata delayed %d subframes\n\n", (int16_t) round( args.syncMdDelay / ( 1000 / IVAS_NUM_FRAMES_PER_SEC / IVAS_MAX_PARAM_SPATIAL_SUBFRAMES ) ) ); } @@ -3509,7 +3520,8 @@ static void parseObjectPosition( *positionDuration = (uint16_t) strtol( line, &endptr, 10 ); readNextMetadataChunk( line, "\n" ); - read_values = (int16_t) sscanf( line, "%f,%f,%f,%f,%f,%f,%f,%f", &meta_prm[0], &meta_prm[1], &meta_prm[2], &meta_prm[3], &meta_prm[4], &meta_prm[5], &meta_prm[6], &meta_prm[7] ); + read_values = (int16_t) sscanf( line, "%f,%f,%f,%f,%f,%f,%f,%f", + &meta_prm[0], &meta_prm[1], &meta_prm[2], &meta_prm[3], &meta_prm[4], &meta_prm[5], &meta_prm[6], &meta_prm[7] ); if ( read_values < 2 ) { diff --git a/lib_isar/lib_isar_post_rend.c b/lib_isar/lib_isar_post_rend.c index c64e59339..e59a27a46 100644 --- a/lib_isar/lib_isar_post_rend.c +++ b/lib_isar/lib_isar_post_rend.c @@ -1735,12 +1735,12 @@ ivas_error ISAR_POST_REND_getSamples( } #ifndef DISABLE_LIMITER - Word32 limiter_thresold; + Word32 limiter_threshold; #ifdef DEBUGGING hIvasRend->numClipping += #endif - limiter_thresold = L_shl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); - limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_thresold, *outAudio.pq_fact ); + limiter_threshold = L_shl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); + limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_threshold, *outAudio.pq_fact ); #endif return IVAS_ERR_OK; diff --git a/lib_isar/lib_isar_pre_rend.c b/lib_isar/lib_isar_pre_rend.c index da4f6542f..10adbea2d 100644 --- a/lib_isar/lib_isar_pre_rend.c +++ b/lib_isar/lib_isar_pre_rend.c @@ -96,9 +96,8 @@ ivas_error ISAR_PRE_REND_open( isCldfbNeeded = 1; } - hSplitRendWrapper->hCldfbHandles = NULL; - - IF( isCldfbNeeded ) + test(); + IF( isCldfbNeeded && hSplitRendWrapper->hCldfbHandles == NULL ) { IF( ( hSplitRendWrapper->hCldfbHandles = (CLDFB_HANDLES_WRAPPER_HANDLE) malloc( sizeof( CLDFB_HANDLES_WRAPPER ) ) ) == NULL ) { diff --git a/lib_rend/ivas_prot_rend_fx.h b/lib_rend/ivas_prot_rend_fx.h index 512d4fd21..fe2f62ed4 100644 --- a/lib_rend/ivas_prot_rend_fx.h +++ b/lib_rend/ivas_prot_rend_fx.h @@ -33,6 +33,7 @@ #ifndef IVAS_PROT_REND_H #define IVAS_PROT_REND_H +#include "typedef.h" #include #include "options.h" #include "ivas_error.h" @@ -1594,6 +1595,40 @@ void ivas_rend_closeCldfbRend( CLDFB_REND_WRAPPER *pCldfbRend ); +/*----------------------------------------------------------------------------------* + * Time domain ring buffer prototypes + *----------------------------------------------------------------------------------*/ + +ivas_error ivas_TD_RINGBUF_Open( + TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ + const UWord32 capacity_per_channel, /* i : Number of samples stored per channel */ + const UWord16 num_channels /* i : Number of channels */ +); + +void ivas_TD_RINGBUF_Close( + TD_RINGBUF_HANDLE *ph /* i/o: Ring buffer handle */ +); + +void ivas_TD_RINGBUF_Push( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const Word32 *data, /* i : Input data */ + const UWord32 num_samples_per_channel /* i : Number of samples per channel to store */ +); + +void ivas_TD_RINGBUF_PushZeros( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const UWord32 num_samples_per_channel /* i : Number of zeros per channel to store */ +); + +void ivas_TD_RINGBUF_Pop( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + Word32 *data, /* i : Output data */ + const UWord32 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ +); + +uint32_t ivas_TD_RINGBUF_Size( + const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ +); /* clang-format on */ diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 85c4dedd2..48e3dd349 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -33,6 +33,7 @@ #ifndef IVAS_STAT_REND_H #define IVAS_STAT_REND_H +#include "stl.h" #include #include "options.h" #include "ivas_cnst.h" @@ -1429,6 +1430,21 @@ typedef struct } CLDFB_REND_WRAPPER; +/*----------------------------------------------------------------------------------* + * Time domain ring buffer structure + *----------------------------------------------------------------------------------*/ + +typedef struct +{ + Word32 *data; /* samples in interleaved layout */ + UWord32 capacity; + UWord16 num_channels; + UWord32 write_pos; + UWord32 read_pos; + Word16 is_full; + +} TD_RINGBUF_DATA, *TD_RINGBUF_HANDLE; + /*----------------------------------------------------------------------------------* * Limiter structure *----------------------------------------------------------------------------------*/ diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index 1f46ca932..ec447df7a 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -129,7 +129,7 @@ ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( /* Functions to be called before/during rendering */ -ivas_error IVAS_REND_NumOutChannels( +ivas_error IVAS_REND_GetNumOutChannels( IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ Word16 *numOutChannels /* o : number of output channels */ ); @@ -213,7 +213,7 @@ ivas_error IVAS_REND_GetHrtfParamBinHandle( ); ivas_error IVAS_REND_GetHrtfStatisticsHandle( - IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS renderer handle */ + IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS renderer handle */ IVAS_DEC_HRTF_STATISTICS_HANDLE **hHrtfStatistics /* o : HRTF statistics handle */ ); @@ -222,7 +222,8 @@ ivas_error IVAS_REND_GetHrtfStatisticsHandle( ivas_error IVAS_REND_FeedInputAudio_fx( IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ const IVAS_REND_InputId inputId, /* i : ID of the input */ - const IVAS_REND_ReadOnlyAudioBuffer inputAudio /* i : buffer with input audio */ + const IVAS_REND_ReadOnlyAudioBuffer inputAudio, /* i : buffer with input audio */ + const bool flushInputs /* i : flush input audio */ ); ivas_error IVAS_REND_FeedInputObjectMetadata( @@ -397,7 +398,7 @@ ivas_error IVAS_REND_GetSamples( /* Functions to be called after rendering */ void IVAS_REND_Close( - IVAS_REND_HANDLE* phIvasRend /* i/o: Pointer to renderer handle */ + IVAS_REND_HANDLE *phIvasRend /* i/o: Pointer to renderer handle */ ); /* Split binaural rendering functions */ diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index edf3231d7..72023e408 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -30,9 +30,14 @@ *******************************************************************************************************/ +#include "basop32.h" #include "basop_util.h" #include "options.h" +#include "cnst.h" +#include "common_api_types.h" +#include "enh32.h" #include "lib_rend.h" +#include "move.h" #include "prot_fx.h" #include "ivas_prot_fx.h" #include "ivas_prot_rend_fx.h" @@ -42,6 +47,7 @@ #include "ivas_cnst.h" #include "ivas_rom_com.h" #include "ivas_rom_rend.h" +#include "stl.h" #include #include #include @@ -90,6 +96,7 @@ typedef struct typedef struct { const Word32 *pOutSampleRate; + const Word32 *pMaxGlobalDelayNs; const AUDIO_CONFIG *pOutConfig; const LSSETUP_CUSTOM_STRUCT *pCustomLsOut; const EFAP_WRAPPER *pEfapOutWrapper; @@ -106,9 +113,11 @@ typedef struct AUDIO_CONFIG inConfig; IVAS_REND_InputId id; IVAS_REND_AudioBuffer inputBuffer; + TD_RINGBUF_HANDLE delayBuffer; Word32 gain_fx; /* Linear, not in dB Q30 */ rendering_context ctx; Word32 numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */ + Word32 delayNumSamples; } input_base; typedef struct @@ -209,6 +218,7 @@ typedef struct hrtf_handles struct IVAS_REND { Word32 sampleRateOut; + Word32 maxGlobalDelayNs; IVAS_LIMITER_HANDLE hLimiter; input_ism inputsIsm[RENDERER_MAX_ISM_INPUTS]; @@ -221,10 +231,10 @@ struct IVAS_REND EFAP_WRAPPER efapOutWrapper; IVAS_LSSETUP_CUSTOM_STRUCT customLsOut; + Word16 splitRendBFI; SPLIT_REND_WRAPPER *splitRendWrapper; IVAS_REND_AudioBuffer splitRendEncBuffer; IVAS_REND_HeadRotData headRotData; - Word16 splitRendBFI; EXTERNAL_ORIENTATION_HANDLE hExternalOrientationData; COMBINED_ORIENTATION_HANDLE hCombinedOrientationData; @@ -267,6 +277,7 @@ static ivas_error allocateInputBaseBufferData_fx( return IVAS_ERR_OK; } + static void freeInputBaseBufferData_fx( Word32 **data /* Qx */ ) { @@ -278,6 +289,17 @@ static void freeInputBaseBufferData_fx( return; } + +static Word16 latencyNsToSamples( + Word32 sampleRate, + Word32 latency_ns ) +{ + Word16 n_samples; + + n_samples = NS2SA_FX2( sampleRate, latency_ns ); + return n_samples; +} + static ivas_error allocateMcLfeDelayBuffer_fx( Word32 **lfeDelayBuffer, /* Qx */ const Word16 data_size ) @@ -290,6 +312,7 @@ static ivas_error allocateMcLfeDelayBuffer_fx( return IVAS_ERR_OK; } + static void freeMcLfeDelayBuffer_fx( Word32 **lfeDelayBuffer /* Qx */ ) { @@ -404,7 +427,7 @@ static void copyBufferToCLDFBarray_fx( static void accumulateCLDFBArrayToBuffer_fx( Word32 re[][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX], Word32 im[][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX], - IVAS_REND_AudioBuffer *buffer ) + const IVAS_REND_AudioBuffer *buffer ) { UWord32 smplIdx, slotIdx; UWord32 numCldfbSamples, num_bands; @@ -1285,6 +1308,9 @@ static void initRendInputBase_fx( inputBase->ctx = rendCtx; inputBase->numNewSamplesPerChannel = 0; move32(); + inputBase->delayNumSamples = 0; + move32(); + inputBase->delayBuffer = NULL; inputBase->inputBuffer.config.numSamplesPerChannel = 0; inputBase->inputBuffer.config.numChannels = 0; @@ -1339,6 +1365,7 @@ static rendering_context getRendCtx( /* Note: when refactoring this, always take the ADDRESS of a member of the * renderer struct, so that the context stores a POINTER to the member, even * if the member is a pointer or handle itself. */ + ctx.pMaxGlobalDelayNs = &hIvasRend->maxGlobalDelayNs; ctx.pOutConfig = &hIvasRend->outputConfig; ctx.pOutSampleRate = &hIvasRend->sampleRateOut; ctx.pCustomLsOut = &hIvasRend->customLsOut; @@ -1383,6 +1410,318 @@ static bool isIoConfigPairSupported( return true; } +static Word32 getRendInputDelayIsm( + const input_ism *inputIsm, + bool splitPreRendCldfb ) +{ + Word32 latency_ns; + latency_ns = 0; + move32(); + (void) ( splitPreRendCldfb ); /* unused */ + + /* set the rendering delay in InputBase */ + latency_ns = L_max( latency_ns, + inputIsm->tdRendWrapper.binaural_latency_ns ); + IF( inputIsm->crendWrapper != NULL ) + { + latency_ns = L_max( latency_ns, + inputIsm->crendWrapper->binaural_latency_ns ); + } + + return latency_ns; +} + + +static void setRendInputDelayIsm( + void *input, + bool splitPreRendCldfb ) +{ + input_ism *inputIsm; + inputIsm = (input_ism *) input; + + inputIsm->base.delayNumSamples = latencyNsToSamples( *inputIsm->base.ctx.pOutSampleRate, + getRendInputDelayIsm( inputIsm, splitPreRendCldfb ) ); +} + + +static Word32 getRendInputDelayMc( + const input_mc *inputMc, + bool splitPreRendCldfb ) +{ + Word32 latency_ns; + latency_ns = 0; + move32(); + (void) ( splitPreRendCldfb ); /* unused */ + + latency_ns = L_max( latency_ns, + inputMc->tdRendWrapper.binaural_latency_ns ); + IF( inputMc->crendWrapper != NULL ) + { + latency_ns = L_max( latency_ns, + inputMc->crendWrapper->binaural_latency_ns ); + } + + return latency_ns; +} + + +static void setRendInputDelayMc( + void *input, + bool splitPreRendCldfb ) +{ + input_mc *inputMc; + inputMc = (input_mc *) input; + + inputMc->base.delayNumSamples = latencyNsToSamples( *inputMc->base.ctx.pOutSampleRate, + getRendInputDelayMc( inputMc, splitPreRendCldfb ) ); +} + + +static Word32 getRendInputDelaySba( + const input_sba *inputSba, + bool splitPreRendCldfb ) +{ + Word32 latency_ns; + + latency_ns = 0; + move32(); + + IF( inputSba->cldfbRendWrapper.hCldfbRend != NULL ) + { + Word32 filterbank_delay; + + filterbank_delay = 0; + move32(); + + IF( EQ_16( splitPreRendCldfb, 0 ) ) + { + filterbank_delay = IVAS_FB_DEC_DELAY_NS; + } + + latency_ns = L_max( latency_ns, + L_add( inputSba->cldfbRendWrapper.binaural_latency_ns, + filterbank_delay ) ); + } + + IF( inputSba->crendWrapper != NULL ) + { + latency_ns = L_max( latency_ns, + inputSba->crendWrapper->binaural_latency_ns ); + } + + return latency_ns; +} + + +static void setRendInputDelaySba( + void *input, + bool splitPreRendCldfb ) +{ + input_sba *inputSba; + inputSba = (input_sba *) input; + + inputSba->base.delayNumSamples = latencyNsToSamples( *inputSba->base.ctx.pOutSampleRate, + getRendInputDelaySba( inputSba, splitPreRendCldfb ) ); +} + + +static Word32 getRendInputDelayMasa( + const input_masa *inputMasa, + bool splitPreRendCldfb ) +{ + Word32 latency_ns; + + latency_ns = 0; + move32(); + + test(); + test(); + test(); + test(); + IF( ( EQ_32( inputMasa->base.inConfig, IVAS_AUDIO_CONFIG_MASA1 ) && + EQ_32( *inputMasa->base.ctx.pOutConfig, IVAS_AUDIO_CONFIG_MONO ) ) || + ( EQ_32( getAudioConfigType( *inputMasa->base.ctx.pOutConfig ), IVAS_REND_AUDIO_CONFIG_TYPE_MASA ) ) ) + { + latency_ns = 0; /* no delay */ + } + ELSE + { + Word32 filterbank_delay; + + filterbank_delay = 0; + move32(); + + /* no delay applied for split rendering */ + IF( EQ_16( splitPreRendCldfb, 0 ) ) + { + filterbank_delay = IVAS_FB_DEC_DELAY_NS; + } + latency_ns = L_max( latency_ns, + filterbank_delay ); + } + return latency_ns; +} + + +static void setRendInputDelayMasa( + void *input, + bool splitPreRendCldfb ) +{ + input_masa *inputMasa; + inputMasa = (input_masa *) input; + + inputMasa->base.delayNumSamples = latencyNsToSamples( *inputMasa->base.ctx.pOutSampleRate, + getRendInputDelayMasa( inputMasa, splitPreRendCldfb ) ); +} + + +static Word32 getMaxGlobalDelayNs( IVAS_REND_CONST_HANDLE hIvasRend ) +{ + Word16 i; + Word32 latency_ns; + Word32 max_latency_ns; + bool splitPreRendCldfb; + + max_latency_ns = 0; + /*assumes that input has been added which means codec has been set to either lcld or lc3plus (even if render config specified default)*/ + IF( hIvasRend->hRendererConfig != NULL ) + { + splitPreRendCldfb = ( hIvasRend->hRendererConfig->split_rend_config.codec == ISAR_SPLIT_REND_CODEC_LCLD ); + } + ELSE + { + splitPreRendCldfb = false; + } + + /* Compute the maximum delay across all inputs */ + FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; i++ ) + { + IF( NE_32( hIvasRend->inputsIsm[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) + { + latency_ns = getRendInputDelayIsm( &hIvasRend->inputsIsm[i], splitPreRendCldfb ); + max_latency_ns = L_max( max_latency_ns, latency_ns ); + } + } + + FOR( i = 0; i < RENDERER_MAX_MC_INPUTS; i++ ) + { + IF( hIvasRend->inputsMc[i].base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) + { + latency_ns = getRendInputDelayMc( &hIvasRend->inputsMc[i], splitPreRendCldfb ); + max_latency_ns = L_max( max_latency_ns, latency_ns ); + } + } + + FOR( i = 0; i < RENDERER_MAX_SBA_INPUTS; i++ ) + { + IF( hIvasRend->inputsSba[i].base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) + { + latency_ns = getRendInputDelaySba( &hIvasRend->inputsSba[i], splitPreRendCldfb ); + max_latency_ns = L_max( max_latency_ns, latency_ns ); + } + } + + FOR( i = 0; i < RENDERER_MAX_MASA_INPUTS; i++ ) + { + IF( hIvasRend->inputsMasa[i].base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) + { + latency_ns = getRendInputDelayMasa( &hIvasRend->inputsMasa[i], splitPreRendCldfb ); + max_latency_ns = L_max( max_latency_ns, latency_ns ); + } + } + + return max_latency_ns; +} + + +static void setMaxGlobalDelayNs( IVAS_REND_HANDLE hIvasRend ) +{ + hIvasRend->maxGlobalDelayNs = getMaxGlobalDelayNs( (IVAS_REND_CONST_HANDLE) hIvasRend ); +} + +static ivas_error alignInputDelay( + input_base *inputBase, + const IVAS_REND_ReadOnlyAudioBuffer inputAudio, + const Word32 maxGlobalDelayNs, + const Word32 sampleRateOut, + const Word16 cldfb2tdSampleFact, + const bool flushInputs ) +{ + ivas_error error; + input_ism *inputIsm; + Word16 maxGlobalDelaySamples; + Word32 numSamplesToPush, numSamplesToPop; + UWord32 ringBufferSize, preDelay; + + maxGlobalDelaySamples = latencyNsToSamples( sampleRateOut, maxGlobalDelayNs ); + maxGlobalDelaySamples = i_mult( maxGlobalDelaySamples, cldfb2tdSampleFact ); + + /* check if we need to open the delay buffer */ + IF( inputBase->delayBuffer == NULL ) + { + /* buffer has to accomodate maxGlobalDelaySamples + 2 * frameSize */ + ringBufferSize = add( maxGlobalDelaySamples, + shl( inputAudio.config.numSamplesPerChannel, 1 ) ); + + /* pre delay for this input is maximum delay - input delay */ + preDelay = maxGlobalDelaySamples - inputBase->delayNumSamples * cldfb2tdSampleFact; + preDelay = sub( maxGlobalDelaySamples, + i_mult( inputBase->delayNumSamples, cldfb2tdSampleFact ) ); + + IF( GT_32( preDelay, 0 ) ) + { + IF( ( error = ivas_TD_RINGBUF_Open( &inputBase->delayBuffer, ringBufferSize, inputAudio.config.numChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* for the first frame we need to push zeros to align the input delay to the global delay + * and then push a frame of actual data */ + ivas_TD_RINGBUF_PushZeros( inputBase->delayBuffer, preDelay ); + + /* for ISM inputs, ensure the metadata sync delay is updated */ + IF( getAudioConfigType( inputBase->inConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED ) + { + inputIsm = (input_ism *) inputBase; + inputIsm->ism_metadata_delay_ms_fx = Mpy_32_32( maxGlobalDelayNs /* Q0 */, 2147 /* (1 / 1e6f) in Q31 */ ); + } + } + } + + IF( inputBase->delayBuffer != NULL ) + { + /* push in the new input data and pop to retrieve a complete input frame + * if we are flushing the inputs, we don't push in any new data */ + IF( NE_16( flushInputs, 0 ) ) + { + numSamplesToPush = 0; + move32(); + + numSamplesToPop = ivas_TD_RINGBUF_Size( inputBase->delayBuffer ); + } + ELSE + { + numSamplesToPush = inputAudio.config.numSamplesPerChannel; + move32(); + + numSamplesToPop = (Word32) inputAudio.config.numSamplesPerChannel; + move32(); + } + + ivas_TD_RINGBUF_Push( inputBase->delayBuffer, inputAudio.data_fx, numSamplesToPush ); + ivas_TD_RINGBUF_Pop( inputBase->delayBuffer, inputBase->inputBuffer.data_fx, numSamplesToPop ); + } + ELSE + { + /* delay buffer isn't open - we don't need it */ + mvl2l( inputAudio.data_fx, + inputBase->inputBuffer.data_fx, + i_mult( inputAudio.config.numSamplesPerChannel, inputAudio.config.numChannels ) ); + } + + return IVAS_ERR_OK; +} + static ivas_error initIsmMasaRendering( input_ism *inputIsm, const Word32 inSampleRate ) @@ -1615,6 +1954,7 @@ static void clearInputIsm( rendCtx = inputIsm->base.ctx; freeInputBaseBufferData_fx( &inputIsm->base.inputBuffer.data_fx ); + ivas_TD_RINGBUF_Close( &inputIsm->base.delayBuffer ); initRendInputBase_fx( &inputIsm->base, IVAS_AUDIO_CONFIG_INVALID, 0, rendCtx, NULL, 0 ); @@ -2609,12 +2949,7 @@ static ivas_error initMcBinauralRendering( move32(); binauralDelayNs = L_max( binauralDelayNs, inputMc->tdRendWrapper.binaural_latency_ns ); - Word16 exp = 0; - move16(); - Word16 var1 = BASOP_Util_Divide3232_Scale( *inputMc->base.ctx.pOutSampleRate, 1000000000, &exp ); - Word32 var2 = L_shr_r( Mpy_32_32( binauralDelayNs, L_deposit_h( var1 ) ), negate( exp ) ); /* Q0 */ - inputMc->binauralDelaySmp = extract_l( var2 ); - move16(); + inputMc->binauralDelaySmp = latencyNsToSamples( *inputMc->base.ctx.pOutSampleRate, binauralDelayNs ); // inputMc->binauralDelaySmp = (int16_t) roundf( (float) binauralDelayNs * *inputMc->base.ctx.pOutSampleRate / 1000000000.f ); IF( GT_16( inputMc->binauralDelaySmp, MAX_BIN_DELAY_SAMPLES ) ) @@ -2820,6 +3155,8 @@ static void clearInputMc( rendCtx = inputMc->base.ctx; freeMcLfeDelayBuffer_fx( &inputMc->lfeDelayBuffer_fx ); freeInputBaseBufferData_fx( &inputMc->bufferData_fx ); + ivas_TD_RINGBUF_Close( &inputMc->base.delayBuffer ); + initRendInputBase_fx( &inputMc->base, IVAS_AUDIO_CONFIG_INVALID, 0, rendCtx, NULL, 0 ); /* Free input's internal handles */ @@ -3120,7 +3457,7 @@ static ivas_error setRendInputActiveSba( return error; } - return error; + return IVAS_ERR_OK; } static void clearInputSba( @@ -3131,6 +3468,7 @@ static void clearInputSba( rendCtx = inputSba->base.ctx; freeInputBaseBufferData_fx( &inputSba->bufferData_fx ); + ivas_TD_RINGBUF_Close( &inputSba->base.delayBuffer ); initRendInputBase_fx( &inputSba->base, IVAS_AUDIO_CONFIG_INVALID, 0, rendCtx, NULL, 0 ); @@ -3220,6 +3558,7 @@ static void clearInputMasa( rendCtx = inputMasa->base.ctx; freeInputBaseBufferData_fx( &inputMasa->bufferData_fx ); + ivas_TD_RINGBUF_Close( &inputMasa->base.delayBuffer ); masaPrerendClose_fx( &inputMasa->hMasaPrerend ); @@ -3287,7 +3626,7 @@ ivas_error IVAS_REND_Open( hIvasRend->num_subframes = num_subframes; /* Initialize limiter */ - IF( NE_32( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) + IF( NE_32( ( error = IVAS_REND_GetNumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) { return error; } @@ -3345,6 +3684,11 @@ ivas_error IVAS_REND_Open( isar_init_split_rend_handles( hIvasRend->splitRendWrapper ); } + hIvasRend->splitRendEncBuffer.data_fx = NULL; + hIvasRend->splitRendEncBuffer.config.is_cldfb = 0; + hIvasRend->splitRendEncBuffer.config.numChannels = 0; + hIvasRend->splitRendEncBuffer.config.numSamplesPerChannel = 0; + FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) { @@ -3552,7 +3896,7 @@ ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( hIvasRend->customLsOut = makeCustomLsSetup( layout ); /* Re-initialize limiter - number of output channels may have changed */ - IF( NE_32( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) + IF( NE_32( ( error = IVAS_REND_GetNumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) { return error; } @@ -3613,7 +3957,7 @@ ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout( * *-------------------------------------------------------------------*/ -ivas_error IVAS_REND_NumOutChannels( +ivas_error IVAS_REND_GetNumOutChannels( IVAS_REND_CONST_HANDLE hIvasRend, Word16 *numOutChannels ) { @@ -3843,7 +4187,7 @@ static Word16 getCldfbRendFlag( const IVAS_REND_AudioConfigType new_configType ) { Word16 i; - Word16 numMasaInputs = 0, numSbaInputs = 0, numIsmInputs = 0, numMcInputs = 0; + Word16 numMasaInputs = 0, numSbaInputs = 0; Word16 isCldfbRend; move16(); @@ -3854,6 +4198,11 @@ static Word16 getCldfbRendFlag( isCldfbRend = 0; move16(); + /* This function is called during three different phases of renderer processing: + * - IVAS_REND_AddInput() + * - IVAS_REND_FeedRenderConfig() + * - IVAS_REND_GetSplitBinauralBitstream() + * Only the last case can assume all inputs are present for the current frame to be rendered */ IF( hIvasRend->hRendererConfig != NULL ) { FOR( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) @@ -3866,23 +4215,8 @@ static Word16 getCldfbRendFlag( numSbaInputs = add( numSbaInputs, ( hIvasRend->inputsSba[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID && new_configType != IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) ? 0 : 1 ); move16(); } - FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) - { - numIsmInputs = add( numIsmInputs, ( hIvasRend->inputsIsm[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID && new_configType != IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED ) ? 0 : 1 ); - move16(); - } - FOR( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) - { - numMcInputs = add( numMcInputs, ( hIvasRend->inputsMc[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID && new_configType != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) ? 0 : 1 ); - move16(); - } - IF( GT_16( numIsmInputs, 0 ) || GT_16( numMcInputs, 0 ) ) - { - isCldfbRend = 0; - move16(); - } - ELSE IF( GT_16( numMasaInputs, 0 ) || ( GT_16( numSbaInputs, 0 ) && EQ_32( hIvasRend->hRendererConfig->split_rend_config.rendererSelection, IVAS_BIN_RENDERER_TYPE_FASTCONV ) ) ) + IF( GT_16( numMasaInputs, 0 ) || ( GT_16( numSbaInputs, 0 ) && EQ_32( hIvasRend->hRendererConfig->split_rend_config.rendererSelection, IVAS_BIN_RENDERER_TYPE_FASTCONV ) ) ) { isCldfbRend = 1; move16(); @@ -3894,12 +4228,12 @@ static Word16 getCldfbRendFlag( /*------------------------------------------------------------------------- - * Function ivas_pre_rend_init() + * Function isar_pre_rend_init() * * *------------------------------------------------------------------------*/ -static ivas_error ivas_pre_rend_init( +static ivas_error isar_pre_rend_init( SPLIT_REND_WRAPPER *pSplitRendWrapper, IVAS_REND_AudioBuffer *pSplitRendEncBuffer, ISAR_SPLIT_REND_CONFIG_DATA *pSplit_rend_config, @@ -3909,11 +4243,21 @@ static ivas_error ivas_pre_rend_init( const Word16 cldfb_in_flag, const Word16 num_subframes ) { + bool realloc; ivas_error error; IVAS_REND_AudioBufferConfig bufConfig; + realloc = false; + + /* only perform init if split rendering output */ test(); - IF( EQ_32( outConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_32( outConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + IF( NE_32( outConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) && NE_32( outConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + { + return IVAS_ERR_OK; + } + + /* these functions should only be called once during initial allocation */ + IF( pSplitRendEncBuffer->data_fx == NULL ) { IF( EQ_32( pSplit_rend_config->poseCorrectionMode, ISAR_SPLIT_REND_POSE_CORRECTION_MODE_CLDFB ) ) { @@ -3928,41 +4272,51 @@ static ivas_error ivas_pre_rend_init( { return error; } + } - /*allocate for CLDFB in and change to TD during process if needed*/ - bufConfig.numSamplesPerChannel = MAX_CLDFB_BUFFER_LENGTH_PER_CHANNEL; - bufConfig.numChannels = i_mult( BINAURAL_CHANNELS, pSplitRendWrapper->multiBinPoseData.num_poses ); - bufConfig.is_cldfb = 1; - pSplitRendEncBuffer->config = bufConfig; + /* We may need to change the allocated buffer size if a new input is added. + * If the cldfb_in_flag is different from what was previously allocated for the buffer, change the size */ + test(); + IF( pSplitRendEncBuffer->data_fx != NULL && ( NE_16( cldfb_in_flag, pSplitRendEncBuffer->config.is_cldfb ) ) ) + { + realloc = true; + } + + test(); + IF( pSplitRendEncBuffer->data_fx == NULL || NE_16( realloc, 0 ) ) + { + /* set buffer config */ + bufConfig.is_cldfb = cldfb_in_flag; move16(); + + bufConfig.numSamplesPerChannel = L_FRAME_MAX; move16(); + IF( NE_16( cldfb_in_flag, 0 ) ) + { + bufConfig.numSamplesPerChannel = MAX_CLDFB_BUFFER_LENGTH_PER_CHANNEL; + move16(); + } + + bufConfig.numChannels = i_mult( BINAURAL_CHANNELS, pSplitRendWrapper->multiBinPoseData.num_poses ); move16(); + pSplitRendEncBuffer->config = bufConfig; move32(); + /* allocate memory */ + IF( NE_16( realloc, 0 ) ) + { + free( pSplitRendEncBuffer->data_fx ); + } + IF( ( pSplitRendEncBuffer->data_fx = malloc( bufConfig.numChannels * bufConfig.numSamplesPerChannel * sizeof( float ) ) ) == NULL ) { return IVAS_ERR_FAILED_ALLOC; } - pSplitRendEncBuffer->q_factor = 0; - pSplitRendEncBuffer->pq_fact = &pSplitRendEncBuffer->q_factor; - } - ELSE - { - IVAS_REND_AudioBufferConfig bufConfig2; - - bufConfig2.numSamplesPerChannel = 0; - bufConfig2.numChannels = 0; - bufConfig2.is_cldfb = 0; - pSplitRendEncBuffer->config = bufConfig2; - pSplitRendEncBuffer->data_fx = NULL; - pSplitRendEncBuffer->pq_fact = NULL; pSplitRendEncBuffer->q_factor = 0; move16(); + pSplitRendEncBuffer->pq_fact = &pSplitRendEncBuffer->q_factor; move16(); - move16(); - move32(); - move32(); } return IVAS_ERR_OK; @@ -3986,7 +4340,11 @@ ivas_error IVAS_REND_AddInput_fx( void *inputsArray; Word32 inputStructSize; ivas_error ( *activateInput )( void *, AUDIO_CONFIG, IVAS_REND_InputId, RENDER_CONFIG_DATA *, hrtf_handles *hrtfs ); + void ( *setInputDelay )( void *, bool ); Word32 inputIndex; + bool splitPreRendCldfb; + splitPreRendCldfb = false; + /* Validate function arguments */ test(); @@ -3995,17 +4353,24 @@ ivas_error IVAS_REND_AddInput_fx( return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - IF( ( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) && hIvasRend->splitRendEncBuffer.data_fx == NULL && hIvasRend->hRendererConfig != NULL ) + IF( hIvasRend->hRendererConfig != NULL ) { Word16 cldfb_in_flag; cldfb_in_flag = getCldfbRendFlag( hIvasRend, getAudioConfigType( inConfig ) ); - IF( ( error = ivas_pre_rend_init( hIvasRend->splitRendWrapper, &hIvasRend->splitRendEncBuffer, &hIvasRend->hRendererConfig->split_rend_config, hIvasRend->headRotData, hIvasRend->sampleRateOut, hIvasRend->outputConfig, cldfb_in_flag, hIvasRend->num_subframes ) ) != IVAS_ERR_OK ) + IF( ( error = isar_pre_rend_init( hIvasRend->splitRendWrapper, + &hIvasRend->splitRendEncBuffer, + &hIvasRend->hRendererConfig->split_rend_config, + hIvasRend->headRotData, hIvasRend->sampleRateOut, + hIvasRend->outputConfig, cldfb_in_flag, + hIvasRend->num_subframes ) ) != IVAS_ERR_OK ) { return error; } } + /*assumes that input has been added which means codec has been set to either lcld or lc3plus (even if render config specified default)*/ + splitPreRendCldfb = EQ_32( hIvasRend->hRendererConfig->split_rend_config.codec, ISAR_SPLIT_REND_CODEC_LCLD ); SWITCH( getAudioConfigType( inConfig ) ) { @@ -4014,6 +4379,7 @@ ivas_error IVAS_REND_AddInput_fx( inputsArray = hIvasRend->inputsIsm; inputStructSize = sizeof( *hIvasRend->inputsIsm ); activateInput = setRendInputActiveIsm; + setInputDelay = setRendInputDelayIsm; move32(); move32(); BREAK; @@ -4022,6 +4388,7 @@ ivas_error IVAS_REND_AddInput_fx( inputsArray = hIvasRend->inputsMc; inputStructSize = sizeof( *hIvasRend->inputsMc ); activateInput = setRendInputActiveMc; + setInputDelay = setRendInputDelayMc; move32(); move32(); BREAK; @@ -4030,6 +4397,7 @@ ivas_error IVAS_REND_AddInput_fx( inputsArray = hIvasRend->inputsSba; inputStructSize = sizeof( *hIvasRend->inputsSba ); activateInput = setRendInputActiveSba; + setInputDelay = setRendInputDelaySba; move32(); move32(); BREAK; @@ -4038,6 +4406,7 @@ ivas_error IVAS_REND_AddInput_fx( inputsArray = hIvasRend->inputsMasa; inputStructSize = sizeof( *hIvasRend->inputsMasa ); activateInput = setRendInputActiveMasa; + setInputDelay = setRendInputDelayMasa; move32(); move32(); BREAK; @@ -4058,6 +4427,15 @@ ivas_error IVAS_REND_AddInput_fx( return error; } +#ifdef CODE_IMPROVEMENTS + setInputDelay( getInputByIndex( inputsArray, inputIndex, inputType ), splitPreRendCldfb ); +#else + setInputDelay( (Word8 *) inputsArray + inputStructSize * inputIndex, splitPreRendCldfb ); +#endif + + /* set global maximum delay after adding an input */ + setMaxGlobalDelayNs( hIvasRend ); + return IVAS_ERR_OK; } @@ -4373,175 +4751,61 @@ ivas_error IVAS_REND_GetInputNumChannels( * * *-------------------------------------------------------------------*/ -ivas_error IVAS_REND_GetNumAllObjects( - IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ - Word16 *numChannels /* o : number of all objects */ -) -{ - test(); - IF( hIvasRend == NULL || numChannels == NULL ) - { - return IVAS_ERR_UNEXPECTED_NULL_POINTER; - } - - test(); - IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_MASA1 ) || EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_MASA2 ) ) - { - *numChannels = (Word16) hIvasRend->inputsIsm[0].total_num_objects; - move16(); - } - - return IVAS_ERR_OK; -} - - -/*-------------------------------------------------------------------* - * IVAS_REND_GetDelay() - * - * - *-------------------------------------------------------------------*/ - -ivas_error IVAS_REND_GetDelay_fx( - IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer state */ - Word16 *nSamples, /* o : Renderer delay in samples */ - Word32 *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */ -) -{ - /* TODO tmu : this function only returns the maximum delay across all inputs - * Ideally each input has its own delay buffer and everything is aligned (binaural and LFE filtering delays are nonuniform) - */ - Word16 i; - Word32 latency_ns; - Word32 max_latency_ns; - - Word32 timescale_by_ns[7] = { 0, 17180, 34360, 0, 68719, 0, 103079 }; - move32(); - move32(); - move32(); - move32(); - move32(); - move32(); - move32(); - - /* Validate function arguments */ - test(); - test(); - IF( hIvasRend == NULL || nSamples == NULL || timeScale == NULL ) - { - return IVAS_ERR_UNEXPECTED_NULL_POINTER; - } - - *timeScale = hIvasRend->sampleRateOut; - move32(); - assert( *timeScale == 8000 || *timeScale == 16000 || *timeScale == 32000 || *timeScale == 48000 ); - *nSamples = 0; - move16(); - max_latency_ns = 0; - move32(); - - /* Compute the maximum delay across all inputs */ - FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; i++ ) - { - IF( NE_32( hIvasRend->inputsIsm[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) - { - IF( hIvasRend->inputsIsm[i].crendWrapper != NULL ) - { - latency_ns = hIvasRend->inputsIsm[i].crendWrapper->binaural_latency_ns; - } - ELSE - { - latency_ns = 0; - } - move32(); - - latency_ns = L_max( latency_ns, hIvasRend->inputsIsm[i].tdRendWrapper.binaural_latency_ns ); - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } - } - - FOR( i = 0; i < RENDERER_MAX_MC_INPUTS; i++ ) - { - IF( NE_32( hIvasRend->inputsMc[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) - { - IF( ( hIvasRend->inputsMc[i].crendWrapper != NULL ) ) - { - latency_ns = hIvasRend->inputsMc[i].crendWrapper->binaural_latency_ns; - } - ELSE - { - latency_ns = 0; - } - move32(); - - latency_ns = L_max( latency_ns, hIvasRend->inputsMc[i].tdRendWrapper.binaural_latency_ns ); - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } - } - - FOR( i = 0; i < RENDERER_MAX_SBA_INPUTS; i++ ) - { - IF( NE_32( hIvasRend->inputsSba[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) - { - test(); - IF( hIvasRend->splitRendWrapper != NULL && hIvasRend->splitRendWrapper->hBinHrSplitPreRend != NULL ) - { - IF( EQ_32( hIvasRend->hRendererConfig->split_rend_config.rendererSelection, IVAS_BIN_RENDERER_TYPE_FASTCONV ) ) - { - latency_ns = hIvasRend->inputsSba[i].cldfbRendWrapper.binaural_latency_ns; - move32(); - } - ELSE - { - IF( ( hIvasRend->inputsSba[i].crendWrapper != NULL ) ) - { - latency_ns = hIvasRend->inputsSba[i].crendWrapper->binaural_latency_ns; - } - ELSE - { - latency_ns = 0; - } - move32(); - } - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } - ELSE IF( hIvasRend->inputsSba[i].cldfbRendWrapper.hCldfbRend != NULL ) - { - latency_ns = hIvasRend->inputsSba[i].cldfbRendWrapper.binaural_latency_ns; - move32(); - latency_ns = L_add( latency_ns, IVAS_FB_DEC_DELAY_NS ); - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } - ELSE - { - IF( hIvasRend->inputsSba[i].crendWrapper != NULL ) - { - latency_ns = hIvasRend->inputsSba[i].crendWrapper->binaural_latency_ns; - } - ELSE - { - latency_ns = 0; - } - move32(); - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } - } +ivas_error IVAS_REND_GetNumAllObjects( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */ + Word16 *numChannels /* o : number of all objects */ +) +{ + test(); + IF( hIvasRend == NULL || numChannels == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - FOR( i = 0; i < RENDERER_MAX_MASA_INPUTS; i++ ) + test(); + IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_MASA1 ) || EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_MASA2 ) ) { - IF( NE_32( hIvasRend->inputsMasa[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) - { - latency_ns = (Word32) ( IVAS_FB_DEC_DELAY_NS ); - move32(); - max_latency_ns = L_max( max_latency_ns, latency_ns ); - } + *numChannels = (Word16) hIvasRend->inputsIsm[0].total_num_objects; + move16(); + } + + return IVAS_ERR_OK; +} + + +/*-------------------------------------------------------------------* + * IVAS_REND_GetDelay() + * + * + *-------------------------------------------------------------------*/ + +ivas_error IVAS_REND_GetDelay_fx( + IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer state */ + Word16 *nSamples, /* o : Renderer delay in samples */ + Word32 *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */ +) +{ + Word32 max_latency_ns; + + /* Validate function arguments */ + test(); + test(); + IF( hIvasRend == NULL || nSamples == NULL || timeScale == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - //*nSamples = (Word16) roundf( (float) max_latency_ns * *timeScale / 1000000000.f ); - Word32 temp = Mpy_32_32( *timeScale, 268436 ); // Q0 + Q31 - Q31 -> Q0, ( 1 / 8000 ) * 2 ^ 31 - *nSamples = extract_l( Mpy_32_32_r( max_latency_ns, timescale_by_ns[temp] ) ); + assert( *timeScale == 8000 || *timeScale == 16000 || *timeScale == 32000 || *timeScale == 48000 ); + + *nSamples = 0; move16(); + *timeScale = hIvasRend->sampleRateOut; + + max_latency_ns = getMaxGlobalDelayNs( hIvasRend ); + + *nSamples = latencyNsToSamples( hIvasRend->sampleRateOut, max_latency_ns ); + return IVAS_ERR_OK; } @@ -4553,14 +4817,16 @@ ivas_error IVAS_REND_GetDelay_fx( *-------------------------------------------------------------------*/ ivas_error IVAS_REND_FeedInputAudio_fx( - IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ - const IVAS_REND_InputId inputId, /* i : ID of the input */ - const IVAS_REND_ReadOnlyAudioBuffer inputAudio /* i : buffer with input audio */ + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_REND_ReadOnlyAudioBuffer inputAudio, /* i : buffer with input audio */ + const bool flushInputs /* i : flush input audio */ ) { ivas_error error; input_base *inputBase; Word16 numInputChannels; + /* Note: this is called cldfb2tdSampleFact in float */ Word16 cldfb2tdShift; /* Validate function arguments */ @@ -4619,7 +4885,16 @@ ivas_error IVAS_REND_FeedInputAudio_fx( inputBase->inputBuffer.config = inputAudio.config; - MVR2R_WORD32( inputAudio.data_fx, inputBase->inputBuffer.data_fx, inputAudio.config.numSamplesPerChannel * inputAudio.config.numChannels ); + if ( ( error = alignInputDelay( + inputBase, + inputAudio, + hIvasRend->maxGlobalDelayNs, + hIvasRend->sampleRateOut, + cldfb2tdShift, + flushInputs ) ) != IVAS_ERR_OK ) + { + return error; + } inputBase->numNewSamplesPerChannel = shr( inputAudio.config.numSamplesPerChannel, cldfb2tdShift ); move32(); @@ -4943,6 +5218,7 @@ Word16 IVAS_REND_FeedRenderConfig( if ( pMasaInput->hMasaExtRend->hDiracDecBin[0] != NULL && pMasaInput->hMasaExtRend->hDiracDecBin[0]->hReverb != NULL ) { ivas_binaural_reverb_close_fx( &pMasaInput->hMasaExtRend->hDiracDecBin[0]->hReverb ); + IF( NE_32( ( error = ivas_binaural_reverb_init_fx( &pMasaInput->hMasaExtRend->hDiracDecBin[0]->hReverb, hIvasRend->hHrtfs.hHrtfStatistics, pMasaInput->hMasaExtRend->hSpatParamRendCom->num_freq_bands, CLDFB_NO_COL_MAX / MAX_PARAM_SPATIAL_SUBFRAMES, &( hRenderConfig->roomAcoustics ), *pMasaInput->base.ctx.pOutSampleRate, NULL, NULL, NULL ) ), IVAS_ERR_OK ) ) { return error; @@ -4951,6 +5227,7 @@ Word16 IVAS_REND_FeedRenderConfig( if ( pMasaInput->hMasaExtRend->hReverb != NULL ) { ivas_binaural_reverb_close_fx( &pMasaInput->hMasaExtRend->hReverb ); + IF( NE_32( ( error = ivas_binaural_reverb_init_fx( &pMasaInput->hMasaExtRend->hReverb, hIvasRend->hHrtfs.hHrtfStatistics, pMasaInput->hMasaExtRend->hSpatParamRendCom->num_freq_bands, CLDFB_NO_COL_MAX / MAX_PARAM_SPATIAL_SUBFRAMES, &( hRenderConfig->roomAcoustics ), *pMasaInput->base.ctx.pOutSampleRate, NULL, NULL, NULL ) ), IVAS_ERR_OK ) ) { return error; @@ -4975,6 +5252,7 @@ Word16 IVAS_REND_FeedRenderConfig( return error; } } + if ( pMcInput->crendWrapper != NULL && pMcInput->crendWrapper->hCrend[0] && pMcInput->crendWrapper->hCrend[0]->hReverb != NULL ) { IF( NE_32( ( error = ivas_reverb_open_fx( &pMcInput->crendWrapper->hCrend[0]->hReverb, hIvasRend->hHrtfs.hHrtfStatistics, hRenderConfig, *pMcInput->base.ctx.pOutSampleRate ) ), IVAS_ERR_OK ) ) @@ -5021,6 +5299,7 @@ Word16 IVAS_REND_FeedRenderConfig( { Word16 cldfb_in_flag; cldfb_in_flag = getCldfbRendFlag( hIvasRend, IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN ); + IF( hIvasRend->splitRendWrapper != NULL ) { ISAR_PRE_REND_close( hIvasRend->splitRendWrapper, &hIvasRend->splitRendEncBuffer ); @@ -5028,7 +5307,7 @@ Word16 IVAS_REND_FeedRenderConfig( hIvasRend->splitRendWrapper = NULL; } - IF( ( error = ivas_pre_rend_init( hIvasRend->splitRendWrapper, &hIvasRend->splitRendEncBuffer, &hIvasRend->hRendererConfig->split_rend_config, hIvasRend->headRotData, hIvasRend->sampleRateOut, hIvasRend->outputConfig, cldfb_in_flag, hIvasRend->num_subframes ) ) != IVAS_ERR_OK ) + IF( ( error = isar_pre_rend_init( hIvasRend->splitRendWrapper, &hIvasRend->splitRendEncBuffer, &hIvasRend->hRendererConfig->split_rend_config, hIvasRend->headRotData, hIvasRend->sampleRateOut, hIvasRend->outputConfig, cldfb_in_flag, hIvasRend->num_subframes ) ) != IVAS_ERR_OK ) { return error; } @@ -6395,8 +6674,10 @@ 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; + Word16 i, ch, slot_idx, num_bands; Word32 tmpBinaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][L_FRAME48k]; + 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]; Word16 output_frame = ismInput->base.inputBuffer.config.numSamplesPerChannel; COMBINED_ORIENTATION_HANDLE pCombinedOrientationData; Word16 ism_md_subframe_update_ext, exp; @@ -6473,6 +6754,7 @@ static ivas_error renderIsmToSplitBinaural( IF( ismInput->hReverb != NULL ) { + exp = add( exp, 2 ); FOR( i = 0; i < BINAURAL_CHANNELS; i++ ) { FOR( Word16 j = 0; j < outAudio.config.numSamplesPerChannel; j++ ) @@ -6482,10 +6764,35 @@ static ivas_error renderIsmToSplitBinaural( } } } - /* 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 ); + + IF( NE_16( outAudio.config.is_cldfb, 0 ) ) + { + /* Perform CLDFB analysis on rendered audio, since the output buffer is CLDFB domain */ + Word16 q_cldfb; + + q_cldfb = exp; + num_bands = extract_l( Mpy_32_32( imult3216( *ismInput->base.ctx.pOutSampleRate, BINAURAL_MAXBANDS ), 44740 /* 1/48000 in Q31 */ ) ); /* Q0 */ + FOR( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) + { + FOR( slot_idx = 0; slot_idx < IVAS_CLDFB_NO_COL_MAX; slot_idx++ ) + { + cldfbAnalysis_ts_fx( &tmpProcessing[ch][num_bands * slot_idx], + &tmpBinaural_CldfbRe[BINAURAL_CHANNELS * pos_idx + ch][slot_idx][0], + &tmpBinaural_CldfbIm[BINAURAL_CHANNELS * pos_idx + ch][slot_idx][0], + num_bands, + ismInput->base.ctx.pSplitRendWrapper->hCldfbHandles->cldfbAna[pos_idx + ch], + &q_cldfb ); + } + } + } + ELSE + { + + /* 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 ); + } /* Overwrite processing buffer with original input audio again */ copyBufferTo2dArray_fx( ismInput->base.inputBuffer, tmpProcessing ); @@ -6497,7 +6804,14 @@ static ivas_error renderIsmToSplitBinaural( Copy_Quat_fx( &originalHeadRot[i], &pCombinedOrientationData->Quaternions[i] ); } - accumulate2dArrayToBuffer_fx( tmpBinaural, &outAudio ); + if ( outAudio.config.is_cldfb ) + { + accumulateCLDFBArrayToBuffer_fx( tmpBinaural_CldfbRe, tmpBinaural_CldfbIm, &outAudio ); + } + else + { + accumulate2dArrayToBuffer_fx( tmpBinaural, &outAudio ); + } pop_wmops(); /* Encoding to split rendering bitstream done at a higher level */ @@ -6553,6 +6867,8 @@ static ivas_error renderInputIsm( { ivas_error error; IVAS_REND_AudioBuffer inAudio; + /* Note: in float this is called cldfb2tdSampleFact - it is either 2 or 1, so we handle it as a shift here */ + Word16 cldfb2tdSampleShift; Word16 exp = *outAudio.pq_fact; move16(); @@ -6560,7 +6876,16 @@ static ivas_error renderInputIsm( move32(); inAudio = ismInput->base.inputBuffer; - IF( NE_32( ismInput->base.numNewSamplesPerChannel, outAudio.config.numSamplesPerChannel ) ) + /* Note: in float this is a factor, so it is 1 or 2 there instead of 0 or 1 here */ + cldfb2tdSampleShift = 0; + move16(); + IF( outAudio.config.is_cldfb ) + { + cldfb2tdSampleShift = 1; + move16(); + } + + IF( NE_32( L_shl( ismInput->base.numNewSamplesPerChannel, cldfb2tdSampleShift ), outAudio.config.numSamplesPerChannel ) ) { return IVAS_ERROR( IVAS_ERR_INVALID_BUFFER_SIZE, "Mismatch between the number of input samples vs number of requested output samples - currently not allowed" ); } @@ -6710,7 +7035,7 @@ static ivas_error renderLfeToBinaural_fx( num_cpy_smpl_cur_frame = sub( frame_size, num_cpy_smpl_prev_frame ); /* Assuming LFE should be delayed by less that the duration of one frame */ - assert( mcInput->binauralDelaySmp < frame_size ); + assert( mcInput->binauralDelaySmp <= MAX_BIN_DELAY_SAMPLES ); /* Get delayed LFE signal from previous frame, apply gain and save in tmp buffer */ v_multc_fixed( mcInput->lfeDelayBuffer_fx, gain_fx, tmpLfeBuffer, num_cpy_smpl_prev_frame ); /* Qx - 1 */ @@ -8684,15 +9009,13 @@ ivas_error IVAS_REND_SetIsmMetadataDelay( *-------------------------------------------------------------------*/ static ivas_error getSamplesInternal( - IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ - IVAS_REND_AudioBuffer outAudio, /* i/o: buffer for output audio */ - IVAS_REND_BitstreamBuffer *hBits /* i/o: buffer for input/output bitstream. Needed in split rendering */ + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + IVAS_REND_AudioBuffer outAudio /* i/o: buffer for output audio */ ) { ivas_error error; Word16 numOutChannels; Word16 cldfb2tdSampleShift; - IVAS_REND_AudioBuffer outAudioOrig; /* Validate function arguments */ test(); @@ -8797,40 +9120,21 @@ static ivas_error getSamplesInternal( } } - IF( NE_32( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) + IF( NE_32( ( error = IVAS_REND_GetNumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) { return error; } - IF( NE_16( numOutChannels, outAudio.config.numChannels ) && NE_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) && NE_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + IF( NE_16( numOutChannels, outAudio.config.numChannels ) && + NE_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) && + NE_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) { return IVAS_ERR_WRONG_NUM_CHANNELS; } - /* Clear original output buffer */ + /* Clear output buffer */ set32_fx( outAudio.data_fx, 0, imult1616( outAudio.config.numChannels, outAudio.config.numSamplesPerChannel ) ); - outAudioOrig = outAudio; - - /* Use internal buffer if outputting split rendering bitstream */ - test(); - IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || - EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) - { - Word16 num_poses_orig; - num_poses_orig = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; - move16(); - outAudio.config = hIvasRend->splitRendEncBuffer.config; - outAudio.data_fx = hIvasRend->splitRendEncBuffer.data_fx; - - ISAR_PRE_REND_GetMultiBinPoseData( &hIvasRend->hRendererConfig->split_rend_config, &hIvasRend->splitRendWrapper->multiBinPoseData, hIvasRend->headRotData.sr_pose_pred_axis ); - - assert( num_poses_orig == hIvasRend->splitRendWrapper->multiBinPoseData.num_poses && "number of poses should not change dynamically" ); - - /* Clear output buffer for split rendering bitstream */ - set32_fx( outAudio.data_fx, 0, outAudio.config.numChannels * outAudio.config.numSamplesPerChannel ); - } - IF( NE_32( ( error = renderActiveInputsIsm( hIvasRend, outAudio ) ), IVAS_ERR_OK ) ) { return error; @@ -8848,150 +9152,6 @@ static ivas_error getSamplesInternal( return error; } - test(); - IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) - { - ISAR_SPLIT_REND_BITS_DATA bits; - Word16 cldfb_in_flag, i, j, k, ch, ro_md_flag; - Word32 Cldfb_RealBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; - Word32 Cldfb_ImagBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; - - FOR( i = 0; i < MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS; i++ ) - { - FOR( j = 0; j < CLDFB_NO_COL_MAX; j++ ) - { - FOR( k = 0; k < CLDFB_NO_CHANNELS_MAX; k++ ) - { - Cldfb_RealBuffer_Binaural[i][j][k] = 0; - Cldfb_ImagBuffer_Binaural[i][j][k] = 0; - move32(); - move32(); - } - } - } - - Word32 *tmpBinaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS], tmpBinaural_buff[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][L_FRAME48k]; - - FOR( ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++ ) - { - tmpBinaural[ch] = tmpBinaural_buff[ch]; - move32(); - } - - IF( EQ_16( outAudio.config.is_cldfb, 1 ) ) - { - cldfb_in_flag = 1; - move16(); - copyBufferToCLDFBarray_fx( outAudio, Cldfb_RealBuffer_Binaural, Cldfb_ImagBuffer_Binaural ); - } - ELSE - { - cldfb_in_flag = 0; - move16(); - copyBufferTo2dArray_fx( outAudio, tmpBinaural_buff ); - } - - /* Encode split rendering bitstream */ - convertBitsBufferToInternalBitsBuff( *hBits, &bits ); - - ro_md_flag = 0; - move16(); - FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) - { - IF( NE_32( hIvasRend->inputsIsm[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) - { - ro_md_flag = 1; - move16(); - break; - } - } - - Word16 q1 = 31, q2 = 31, Q_buff; - Word16 Q_out[CLDFB_NO_COL_MAX]; - Q_out[0] = 31; - Word16 num_poses = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; - - for ( i = 0; i < num_poses * BINAURAL_CHANNELS; i++ ) - { - for ( j = 0; j < CLDFB_NO_COL_MAX; j++ ) - { - q1 = s_min( q1, L_norm_arr( Cldfb_RealBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX ) ); - q2 = s_min( q2, L_norm_arr( Cldfb_ImagBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX ) ); - } - } - Q_buff = s_min( q1, q2 ); - for ( i = 0; i < num_poses * BINAURAL_CHANNELS; i++ ) - { - for ( j = 0; j < CLDFB_NO_COL_MAX; j++ ) - { - scale_sig32( Cldfb_RealBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX, Q_buff ); - scale_sig32( Cldfb_ImagBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX, Q_buff ); - } - } - Q_buff = Q_buff + *outAudio.pq_fact; - - IF( EQ_16( cldfb_in_flag, 0 ) ) - { - /*TD input*/ - num_poses = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; - - FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; ++i ) - { - Q_out[0] = s_min( Q_out[0], L_norm_arr( tmpBinaural_buff[i], L_FRAME48k ) ); - } - - FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; ++i ) - { - scale_sig32( tmpBinaural_buff[i], L_FRAME48k, Q_out[0] ); - } - - Q_out[0] = Q_out[0] + *outAudio.pq_fact; - } - - if ( ( error = ISAR_PRE_REND_MultiBinToSplitBinaural( hIvasRend->splitRendWrapper, hIvasRend->headRotData.headPositions[0], hIvasRend->hRendererConfig->split_rend_config.splitRendBitRate, hIvasRend->hRendererConfig->split_rend_config.codec, - hIvasRend->hRendererConfig->split_rend_config.isar_frame_size_ms, - hIvasRend->hRendererConfig->split_rend_config.codec_frame_size_ms, -#ifdef FIX_2083_FLOATING_POINT_LEFTOVERS - &bits, Cldfb_RealBuffer_Binaural, Cldfb_ImagBuffer_Binaural, extract_l( Mpy_32_32( hIvasRend->sampleRateOut, 2684355 /*(BINAURAL_MAXBANDS / 48000) in Q31*/ ) ), tmpBinaural, 1, cldfb_in_flag, ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, ro_md_flag, Q_buff, &Q_out[0] ) ) != IVAS_ERR_OK ) -#else - &bits, Cldfb_RealBuffer_Binaural, Cldfb_ImagBuffer_Binaural, ( const Word16 )( ( BINAURAL_MAXBANDS * hIvasRend->sampleRateOut ) / 48000 ), tmpBinaural, 1, cldfb_in_flag, ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, ro_md_flag, Q_buff, &Q_out[0] ) ) != IVAS_ERR_OK ) -#endif - { - return error; - } - - Word16 pcm_out_flag = ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0; - IF( NE_16( pcm_out_flag, 0 ) ) - { - FOR( j = 0; j < BINAURAL_CHANNELS; j++ ) - { - scale_sig32( tmpBinaural_buff[j], L_FRAME48k, sub( *outAudio.pq_fact, Q_out[j] ) ); // *outAudio.pq_fact - } - } - - convertInternalBitsBuffToBitsBuffer( hBits, bits ); - - /* reset to outAudioOrig in case of PCM output */ - outAudio.config = outAudioOrig.config; - outAudio.data_fx = outAudioOrig.data_fx; - - IF( NE_16( pcm_out_flag, 0 ) ) - { - accumulate2dArrayToBuffer_fx( tmpBinaural_buff, &outAudio ); - } - } - - if ( outAudio.config.is_cldfb == 0 ) - { - Word32 limiter_thresold = L_lshl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); -#ifndef DISABLE_LIMITER - limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_thresold, *outAudio.pq_fact ); -#endif - } - - /* update global cominbed orientation start index */ - ivas_combined_orientation_update_start_index( hIvasRend->hCombinedOrientationData, outAudio.config.numSamplesPerChannel ); - return IVAS_ERR_OK; } @@ -9001,13 +9161,31 @@ static ivas_error getSamplesInternal( * * *-------------------------------------------------------------------*/ - ivas_error IVAS_REND_GetSamples( IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ IVAS_REND_AudioBuffer outAudio /* i/o: buffer for output audio */ ) { - return getSamplesInternal( hIvasRend, outAudio, NULL ); + ivas_error error; + + IF( ( error = getSamplesInternal( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + + + IF( NE_16( outAudio.config.is_cldfb, 0 ) ) + { + Word32 limiter_threshold = L_lshl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); +#ifndef DISABLE_LIMITER + limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_threshold, *outAudio.pq_fact ); +#endif + } + + /* update global cominbed orientation start index */ + ivas_combined_orientation_update_start_index( hIvasRend->hCombinedOrientationData, outAudio.config.numSamplesPerChannel ); + + return IVAS_ERR_OK; } @@ -9023,25 +9201,149 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( IVAS_REND_BitstreamBuffer *hBits /* o : buffer for output bitstream */ ) { + ivas_error error; + Word16 ch; Word16 cldfb_in_flag; + Word16 i, ro_md_flag; + Word16 num_poses_orig; + Word32 *tmpBinaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS], tmpBinaural_buff[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][L_FRAME48k]; + Word32 Cldfb_ImagBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; + Word32 Cldfb_RealBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX][CLDFB_NO_CHANNELS_MAX]; + IVAS_REND_AudioBufferConfig *pSplitEncBufConfig; + ISAR_SPLIT_REND_CONFIG_HANDLE pSplitRendConfig; + ISAR_SPLIT_REND_BITS_DATA bits; + Word16 max_bands; + Word16 Q_out[2]; + + max_bands = 0; + move16(); + + Q_out[0] = Q31; + move16(); + Q_out[1] = Q31; + move16(); + + FOR( ch = 0; ch < i_mult( MAX_HEAD_ROT_POSES, BINAURAL_CHANNELS ); ch++ ) + { + tmpBinaural[ch] = tmpBinaural_buff[ch]; + } cldfb_in_flag = getCldfbRendFlag( hIvasRend, IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN ); - hIvasRend->splitRendEncBuffer.config.is_cldfb = cldfb_in_flag; + pSplitEncBufConfig = &hIvasRend->splitRendEncBuffer.config; + pSplitRendConfig = &hIvasRend->hRendererConfig->split_rend_config; + + /* 0 DoF / No pose correction retains frame size */ + pSplitEncBufConfig->is_cldfb = cldfb_in_flag; + move16(); + + test(); + IF( EQ_16( pSplitRendConfig->dof, 0 ) || EQ_32( pSplitRendConfig->poseCorrectionMode, ISAR_SPLIT_REND_POSE_CORRECTION_MODE_NONE ) ) + { + pSplitEncBufConfig->numSamplesPerChannel = outAudio.config.numSamplesPerChannel; + move16(); + } + /* Pose correction requires 20ms */ + ELSE + { + pSplitEncBufConfig->numSamplesPerChannel = div_s( hIvasRend->sampleRateOut, FRAMES_PER_SEC ); + } - if ( hIvasRend->hRendererConfig->split_rend_config.dof == 0 || hIvasRend->hRendererConfig->split_rend_config.poseCorrectionMode == ISAR_SPLIT_REND_POSE_CORRECTION_MODE_NONE ) + /* Note: float was "pSplitEncBufConfig->numSamplesPerChannel *= cldfb_in_flag ? 2 : 1;" */ + IF( NE_16( cldfb_in_flag, 0 ) ) { - hIvasRend->splitRendEncBuffer.config.numSamplesPerChannel = outAudio.config.numSamplesPerChannel; + pSplitEncBufConfig->numSamplesPerChannel = shl( pSplitEncBufConfig->numSamplesPerChannel, 1 ); } - else + + num_poses_orig = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; + move16(); + + ISAR_PRE_REND_GetMultiBinPoseData( pSplitRendConfig, + &hIvasRend->splitRendWrapper->multiBinPoseData, + hIvasRend->headRotData.sr_pose_pred_axis ); + assert( num_poses_orig == hIvasRend->splitRendWrapper->multiBinPoseData.num_poses && "number of poses should not change dynamically" ); + + /* hIvasRend->splitRendEncBuffer contains multi-pose data for BINAURAL_SPLIT_CODED output + outAudio used later for main pose BINAURAL_SPLIT_PCM output */ + IF( ( error = getSamplesInternal( hIvasRend, hIvasRend->splitRendEncBuffer ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* copy outputs */ + IF( NE_16( hIvasRend->splitRendEncBuffer.config.is_cldfb, 1 ) ) + { + cldfb_in_flag = 1; + move16(); + copyBufferToCLDFBarray_fx( hIvasRend->splitRendEncBuffer, Cldfb_RealBuffer_Binaural, Cldfb_ImagBuffer_Binaural ); + } + ELSE + { + cldfb_in_flag = 0; + move16(); + copyBufferTo2dArray_fx( hIvasRend->splitRendEncBuffer, tmpBinaural_buff ); + } + + /* Encode split rendering bitstream */ + convertBitsBufferToInternalBitsBuff( *hBits, &bits ); + + ro_md_flag = 0; + move16(); + FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + IF( NE_32( hIvasRend->inputsIsm[i].base.inConfig, IVAS_AUDIO_CONFIG_INVALID ) ) + { + ro_md_flag = 1; + + break; + } + } + + max_bands = extract_l( Mpy_32_32( imult3216( hIvasRend->sampleRateOut, BINAURAL_MAXBANDS ), 44740 /* 1/48000 in Q31 */ ) ); + IF( ( error = ISAR_PRE_REND_MultiBinToSplitBinaural( hIvasRend->splitRendWrapper, + hIvasRend->headRotData.headPositions[0], + pSplitRendConfig->splitRendBitRate, + pSplitRendConfig->codec, + pSplitRendConfig->isar_frame_size_ms, + pSplitRendConfig->codec_frame_size_ms, + &bits, + Cldfb_RealBuffer_Binaural, + Cldfb_ImagBuffer_Binaural, + max_bands, + tmpBinaural, + 1, + cldfb_in_flag, + ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, + ro_md_flag, + hIvasRend->splitRendEncBuffer.q_factor, + Q_out ) ) != IVAS_ERR_OK ) + { + return error; + } + + convertInternalBitsBuffToBitsBuffer( hBits, bits ); + + /* copy over first pose data to outAudio */ + IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + { + /* set outAudio to zero - getSamplesInternal only cleared splitRendEncBuffer */ + set_zero_fx( outAudio.data_fx, i_mult( outAudio.config.numChannels, outAudio.config.numSamplesPerChannel ) ); + accumulate2dArrayToBuffer_fx( tmpBinaural_buff, &outAudio ); + } + + IF( EQ_16( outAudio.config.is_cldfb, 0 ) ) { - hIvasRend->splitRendEncBuffer.config.numSamplesPerChannel = (Word16) ( hIvasRend->sampleRateOut / FRAMES_PER_SEC ); +#ifndef DISABLE_LIMITER +#ifdef DEBUGGING + hIvasRend->numClipping += +#endif + limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); +#endif } - hIvasRend->splitRendEncBuffer.config.numSamplesPerChannel *= cldfb_in_flag ? 2 : 1; + /* update global cominbed orientation start index */ + ivas_combined_orientation_update_start_index( hIvasRend->hCombinedOrientationData, outAudio.config.numSamplesPerChannel ); - /* hIvasRend->splitRendEncBuffer used for BINAURAL_SPLIT_CODED output - outAudio used for BINAURAL_SPLIT_PCM output */ - return getSamplesInternal( hIvasRend, outAudio, hBits ); + return IVAS_ERR_OK; } -- GitLab From 5b3981608a145322782852d7bb155b3f49c81538 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 16 Oct 2025 17:07:51 +0200 Subject: [PATCH 02/36] add ring buffer file --- lib_rend/ivas_td_ring_buffer_fx.c | 346 ++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 lib_rend/ivas_td_ring_buffer_fx.c diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c new file mode 100644 index 000000000..0660bc506 --- /dev/null +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -0,0 +1,346 @@ +/****************************************************************************************************** + + (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +#include "basop32.h" +#include "move.h" +#include "typedef.h" +#include +#include +#include "ivas_error_utils.h" +#include "ivas_prot_rend_fx.h" +#include "wmc_auto.h" + + +/*-----------------------------------------------------------------------* + * Local function prototypes + *-----------------------------------------------------------------------*/ + +static UWord32 ivas_td_ringbuf_total_size( + TD_RINGBUF_HANDLE h ) +{ + UWord32 total_size; + + IF( h->is_full ) + { + total_size = h->capacity; + } + + IF( h->read_pos <= h->write_pos ) + { + total_size = L_sub( h->write_pos, h->read_pos ); + } + /* else wrap around */ + total_size = L_add( h->write_pos, L_sub( h->capacity, h->read_pos ) ); + + return total_size; +} + + +static Word16 ivas_td_ringbuf_has_space_for_num_samples( + TD_RINGBUF_HANDLE h, + const UWord32 num_samples ) +{ + // float was : return (Word16) ( ivas_td_ringbuf_total_size( h ) + num_samples <= h->capacity ); + Word16 has_space; + UWord32 total_size_after; + + move16(); + has_space = 0; + + total_size_after = L_add( ivas_td_ringbuf_total_size( h ), num_samples ); + IF( LE_32( total_size_after, h->capacity ) ) + { + has_space = 1; + } + return has_space; +} + + +/*-----------------------------------------------------------------------* + * Global function definitions + *-----------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_Open() + * + * Allocate a ring buffer for TD data with the given capacity of TD samples per channel. + * + * May return IVAS_ERR_FAILED_ALLOC on failed allocation, or IVAS_ERR_OK otherwise. + *---------------------------------------------------------------------*/ + +ivas_error ivas_TD_RINGBUF_Open( + TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ + const UWord32 capacity_per_channel, /* i : Number of samples stored per channel */ + const UWord16 num_channels /* i : Number of channels */ +) +{ + TD_RINGBUF_HANDLE h; + UWord32 capacity; + + // TODO: how to integer multiplication 32 x 16 ? + capacity = L_mult0( capacity_per_channel, num_channels ); + + h = malloc( sizeof( TD_RINGBUF_DATA ) ); + IF( h == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); + } + // TODO: move for pointer assignment? + h->data = NULL; + move32(); + h->capacity = 0; + move16(); + h->num_channels = num_channels; + move32(); + h->write_pos = 0; + move32(); + h->read_pos = 0; + move16(); + h->is_full = 0; + // TODO: move for pointer assignment? + *ph = h; + + h->data = malloc( capacity * sizeof( Word32 ) ); + IF( h->data == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); + } + + move32(); + h->capacity = capacity; + + return IVAS_ERR_OK; +} + + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_Close() + * + * Dellocate TD ring buffer. The given handle will be set to NULL. + + *---------------------------------------------------------------------*/ + +void ivas_TD_RINGBUF_Close( + TD_RINGBUF_HANDLE *ph /* i/o: Ring buffer handle */ +) +{ + TD_RINGBUF_HANDLE h; + + IF( ph == NULL ) + { + return; + } + // TODO: move for pointer assignment? + h = *ph; + + IF( h == NULL ) + { + return; + } + + IF( h->data != NULL ) + { + free( h->data ); + } + + free( h ); + // TODO: move for pointer assignment? + *ph = NULL; + + return; +} + + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_Push() + * + * Push samples onto the back of the TD ring buffer. + * Returns total number of buffered samples (includes number of channels) + *---------------------------------------------------------------------*/ + +void ivas_TD_RINGBUF_Push( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const Word32 *data, /* i : Input data */ + const UWord32 num_samples_per_channel /* i : Number of samples per channel to store */ +) +{ + UWord32 s; + UWord16 c; + + // TODO: integer multiplication for 32 * 16? + assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + + FOR( s = 0; s < num_samples_per_channel; ++s ) + { + FOR( c = 0; c < h->num_channels; ++c ) + { + UWord32 shift_val; + + /* pointer is advance by c * num_samples_per_channel + s */ + shift_val = L_mult0( c, num_samples_per_channel ); + shift_val = L_add( s, shift_val ); + + // TODO: move for pointer assignment? + h->data[h->write_pos] = *( data + shift_val ); + ++h->write_pos; + + IF( EQ_32( h->write_pos, h->capacity ) ) + { + move32(); + h->write_pos = 0; + } + } + } + + IF( EQ_32( h->read_pos, h->write_pos ) ) + { + move32(); + h->is_full = 1; + } + + return; +} + + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_PushZeros() + * + * Push zero samples onto the back of the TD ring buffer. + *---------------------------------------------------------------------*/ + +void ivas_TD_RINGBUF_PushZeros( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const UWord32 num_samples_per_channel /* i : Number of zeros per channel to store */ +) +{ + UWord32 s; + UWord16 c; + + // TODO: integer multiplication for 32 * 16? + assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + IF( EQ_32( num_samples_per_channel, 0 ) ) + { + return; + } + + FOR( s = 0; s < num_samples_per_channel; ++s ) + { + FOR( c = 0; c < h->num_channels; ++c ) + { + // TODO: move for pointer assignment? + h->data[h->write_pos] = 0; + ++h->write_pos; + + IF( EQ_32( h->write_pos, h->capacity ) ) + { + move32(); + h->write_pos = 0; + } + } + } + + IF( EQ_32( h->read_pos, h->write_pos ) ) + { + move32(); + h->is_full = 1; + } + + return; +} + + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_Pop() + * + * Pop samples from the front of the TD ring buffer. + *---------------------------------------------------------------------*/ + +void ivas_TD_RINGBUF_Pop( + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + Word32 *data, /* i : Output data */ + const UWord32 num_samples_per_channel /* i : Number of samples per channel to retrieve */ +) +{ + UWord32 s; + UWord16 c; + Word32 requested_samples; + + // TODO + requested_samples = L_mult0( num_samples_per_channel, h->num_channels ); + assert( GE_32( ivas_td_ringbuf_total_size( h ), requested_samples ) ); + + FOR( s = 0; s < num_samples_per_channel; ++s ) + { + FOR( c = 0; c < h->num_channels; ++c ) + { + UWord32 shift_val; + + /* pointer is advanced by c * num_samples_per_channel + s */ + shift_val = L_mult0( c, num_samples_per_channel ); + shift_val = L_add( s, shift_val ); + + *( data + shift_val ) = h->data[h->read_pos]; + ++h->read_pos; + + IF( EQ_32( h->write_pos, h->capacity ) ) + { + move32(); + h->write_pos = 0; + } + } + } + + IF( NE_16( h->is_full, 0 ) ) + { + move16(); + h->is_full = 0; + } + + return; +} + + +/*---------------------------------------------------------------------* + * ivas_TD_RINGBUF_Size() + * + * Returns number of buffered samples per channel. + *---------------------------------------------------------------------*/ + +UWord32 ivas_TD_RINGBUF_Size( + const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ +) +{ + UWord32 size; + + size = div_l( ivas_td_ringbuf_total_size( h ), h->num_channels ); + return size; +} -- GitLab From 93d61051979ff70dbe855791c12a0cea18d152ab Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Mon, 20 Oct 2025 14:14:33 +0200 Subject: [PATCH 03/36] Improve documentation comments for TD ring buffer --- lib_rend/ivas_stat_rend.h | 4 ++-- lib_rend/ivas_td_ring_buffer_fx.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 48e3dd349..3c38267b7 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -1436,8 +1436,8 @@ typedef struct typedef struct { - Word32 *data; /* samples in interleaved layout */ - UWord32 capacity; + Word32 *data; /* samples in interleaved layout, e.g. for channels A, B, C, samples are stored: A1, B1, C1, A2, B2, C2, ... */ + UWord32 capacity; /* max number of Word32 values that can be stored */ UWord16 num_channels; UWord32 write_pos; UWord32 read_pos; diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 0660bc506..23ef8e323 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -44,6 +44,12 @@ * Local function prototypes *-----------------------------------------------------------------------*/ +/*---------------------------------------------------------------------* + * ivas_td_ringbuf_total_size() + * + * Returns total number of buffered samples (including number of channels) + *---------------------------------------------------------------------*/ + static UWord32 ivas_td_ringbuf_total_size( TD_RINGBUF_HANDLE h ) { -- GitLab From 58a65585fc58f3f482905ee9ccdfce86320d36a0 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 23 Oct 2025 11:03:33 +0200 Subject: [PATCH 04/36] apply some review suggestions --- lib_rend/ivas_td_ring_buffer_fx.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 23ef8e323..4e82dcb8b 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -113,14 +113,15 @@ ivas_error ivas_TD_RINGBUF_Open( UWord32 capacity; // TODO: how to integer multiplication 32 x 16 ? - capacity = L_mult0( capacity_per_channel, num_channels ); + capacity = Mult_32_16( capacity_per_channel, num_channels ); h = malloc( sizeof( TD_RINGBUF_DATA ) ); IF( h == NULL ) { return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } - // TODO: move for pointer assignment? + + move32(); h->data = NULL; move32(); h->capacity = 0; @@ -132,7 +133,7 @@ ivas_error ivas_TD_RINGBUF_Open( h->read_pos = 0; move16(); h->is_full = 0; - // TODO: move for pointer assignment? + move32(); *ph = h; h->data = malloc( capacity * sizeof( Word32 ) ); @@ -165,7 +166,8 @@ void ivas_TD_RINGBUF_Close( { return; } - // TODO: move for pointer assignment? + + move32(); h = *ph; IF( h == NULL ) @@ -202,8 +204,7 @@ void ivas_TD_RINGBUF_Push( UWord32 s; UWord16 c; - // TODO: integer multiplication for 32 * 16? - assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + assert( ivas_td_ringbuf_has_space_for_num_samples( h, Mult_32_16( num_samples_per_channel, h->num_channels ) ) ); FOR( s = 0; s < num_samples_per_channel; ++s ) { @@ -215,7 +216,7 @@ void ivas_TD_RINGBUF_Push( shift_val = L_mult0( c, num_samples_per_channel ); shift_val = L_add( s, shift_val ); - // TODO: move for pointer assignment? + move32(); h->data[h->write_pos] = *( data + shift_val ); ++h->write_pos; @@ -251,8 +252,7 @@ void ivas_TD_RINGBUF_PushZeros( UWord32 s; UWord16 c; - // TODO: integer multiplication for 32 * 16? - assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + assert( ivas_td_ringbuf_has_space_for_num_samples( h, Mult_32_16( num_samples_per_channel, h->num_channels ) ) ); IF( EQ_32( num_samples_per_channel, 0 ) ) { return; @@ -262,7 +262,7 @@ void ivas_TD_RINGBUF_PushZeros( { FOR( c = 0; c < h->num_channels; ++c ) { - // TODO: move for pointer assignment? + move32(); h->data[h->write_pos] = 0; ++h->write_pos; -- GitLab From 9a409126e4b9cf3c2bf9409821d135f00723277b Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 23 Oct 2025 11:07:07 +0200 Subject: [PATCH 05/36] apply some more review suggestions --- lib_rend/ivas_td_ring_buffer_fx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 4e82dcb8b..6e34ab31c 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -112,7 +112,6 @@ ivas_error ivas_TD_RINGBUF_Open( TD_RINGBUF_HANDLE h; UWord32 capacity; - // TODO: how to integer multiplication 32 x 16 ? capacity = Mult_32_16( capacity_per_channel, num_channels ); h = malloc( sizeof( TD_RINGBUF_DATA ) ); @@ -181,8 +180,8 @@ void ivas_TD_RINGBUF_Close( } free( h ); - // TODO: move for pointer assignment? *ph = NULL; + move32(); return; } @@ -300,8 +299,7 @@ void ivas_TD_RINGBUF_Pop( UWord16 c; Word32 requested_samples; - // TODO - requested_samples = L_mult0( num_samples_per_channel, h->num_channels ); + requested_samples = Mult_32_16( num_samples_per_channel, h->num_channels ); assert( GE_32( ivas_td_ringbuf_total_size( h ), requested_samples ) ); FOR( s = 0; s < num_samples_per_channel; ++s ) -- GitLab From eb72f4e633dd8a15477d08ce9aa44d14d1a750f9 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Wed, 29 Oct 2025 18:11:05 +0100 Subject: [PATCH 06/36] align with float + conversion from nano to mili --- apps/renderer.c | 8 ++++---- lib_rend/lib_rend_fx.c | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 7f9744c82..29ac7499a 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -191,7 +191,7 @@ typedef struct float lfeConfigElevation; bool lfeCustomRoutingEnabled; char inLfePanningMatrixFile[RENDERER_MAX_CLI_ARG_LENGTH]; - float syncMdDelay; + int16_t syncMdDelay; IVAS_RENDER_FRAMESIZE render_framesize; uint16_t directivityPatternId[RENDERER_MAX_ISM_INPUTS]; AcousticEnvironmentSequence aeSequence; @@ -1278,7 +1278,7 @@ int main( fprintf( stderr, "\nError in IVAS_REND_SetTotalNumberOfObjects(): %s\n", ivas_error_to_string( error ) ); goto cleanup; } - Word32 var1 = (Word32) ( args.syncMdDelay ); + Word16 var1 = (Word16) ( args.syncMdDelay ); IF( ( error = IVAS_REND_SetIsmMetadataDelay( hIvasRend, var1 ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_REND_SetIsmMetadataDelay(): %s\n", ivas_error_to_string( error ) ); @@ -2123,7 +2123,7 @@ int main( if ( args.inConfig.numAudioObjects != 0 && ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) ) { - fprintf( stdout, "\n\nMetadata delayed %d subframes\n\n", (int16_t) round( args.syncMdDelay / ( 1000 / IVAS_NUM_FRAMES_PER_SEC / IVAS_MAX_PARAM_SPATIAL_SUBFRAMES ) ) ); + fprintf( stdout, "\n\nMetadata delayed %d subframes\n\n", (int16_t) round( args.syncMdDelay / ( float BINAURAL_RENDERING_FRAME_SIZE_MS ) ) ); } if ( !args.quietModeEnabled && args.delayCompensationEnabled ) @@ -3023,7 +3023,7 @@ static void parseOption( case CmdLnOptionId_syncMdDelay: assert( numOptionValues == 1 ); /* Metadata Delay to sync with audio delay in ms */ - args->syncMdDelay = strtof( optionValues[0], NULL ); + args->syncMdDelay = (int16_t) strtol( optionValues[0], NULL, 10 ); break; default: assert( 0 && "This should be unreachable - all command line options should be explicitly handled." ); diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index bd5a19c8b..4bf358a58 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -48,6 +48,7 @@ #include "ivas_rom_com.h" #include "ivas_rom_rend.h" #include "stl.h" +#include "typedef.h" #include #include #include @@ -137,7 +138,7 @@ typedef struct #ifdef NONBE_1377_REND_DIRATT_CONF Word16 object_id; #endif - Word32 ism_metadata_delay_ms_fx; /* Q0 */ + Word16 ism_metadata_delay_ms_fx; /* Q0 */ } input_ism; typedef struct @@ -1674,8 +1675,11 @@ static ivas_error alignInputDelay( /* for ISM inputs, ensure the metadata sync delay is updated */ IF( getAudioConfigType( inputBase->inConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED ) { + Word64 tmp; inputIsm = (input_ism *) inputBase; - inputIsm->ism_metadata_delay_ms_fx = Mpy_32_32( maxGlobalDelayNs /* Q0 */, 2147 /* (1 / 1e6f) in Q31 */ ); + + tmp = W_mult0_32_32( L_add( maxGlobalDelayNs, 500000 ) /* + 0.5ms for rounding */, /* Q0 */, 2147 /* (1 / 1e6f) in Q31 */ ); // Q31 + inputIsm->ism_metadata_delay_ms_fx = add( inputIsm->ism_metadata_delay_ms_fx, extract_h( W_extract_h( tmp ) ) ); } } } @@ -9053,7 +9057,7 @@ ivas_error IVAS_REND_SetTotalNumberOfObjects( ivas_error IVAS_REND_SetIsmMetadataDelay( IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS renderer handle */ - const Word32 sync_md_delay /* i : ISM Metadata Delay in ms to sync with audio delay */ + const Word16 sync_md_delay /* i : ISM Metadata Delay in ms to sync with audio delay */ ) { Word16 i; @@ -9065,7 +9069,7 @@ ivas_error IVAS_REND_SetIsmMetadataDelay( FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) { hIvasRend->inputsIsm[i].ism_metadata_delay_ms_fx = sync_md_delay; - move32(); + move16(); } return IVAS_ERR_OK; -- GitLab From bd805e86e15fe94b48bf7dfa6bc8e83b7a996778 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Wed, 29 Oct 2025 18:24:18 +0100 Subject: [PATCH 07/36] fix compilation errors --- apps/renderer.c | 5 ++--- lib_rend/lib_rend.h | 2 +- lib_rend/lib_rend_fx.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 29ac7499a..691bc178c 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1278,8 +1278,7 @@ int main( fprintf( stderr, "\nError in IVAS_REND_SetTotalNumberOfObjects(): %s\n", ivas_error_to_string( error ) ); goto cleanup; } - Word16 var1 = (Word16) ( args.syncMdDelay ); - IF( ( error = IVAS_REND_SetIsmMetadataDelay( hIvasRend, var1 ) ) != IVAS_ERR_OK ) + IF( ( error = IVAS_REND_SetIsmMetadataDelay( hIvasRend, args.syncMdDelay ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_REND_SetIsmMetadataDelay(): %s\n", ivas_error_to_string( error ) ); goto cleanup; @@ -2123,7 +2122,7 @@ int main( if ( args.inConfig.numAudioObjects != 0 && ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB ) ) { - fprintf( stdout, "\n\nMetadata delayed %d subframes\n\n", (int16_t) round( args.syncMdDelay / ( float BINAURAL_RENDERING_FRAME_SIZE_MS ) ) ); + fprintf( stdout, "\n\nMetadata delayed %d subframes\n\n", (int16_t) round( args.syncMdDelay / ( (float) BINAURAL_RENDERING_FRAME_SIZE_MS ) ) ); } if ( !args.quietModeEnabled && args.delayCompensationEnabled ) diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index a45fce272..f5f1d86cb 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -379,7 +379,7 @@ ivas_error IVAS_REND_SetTotalNumberOfObjects( ivas_error IVAS_REND_SetIsmMetadataDelay( IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS renderer handle */ - const Word32 sync_md_delay /* i : Metadata Delay in ms to sync with audio delay */ + const Word16 sync_md_delay /* i : Metadata Delay in ms to sync with audio delay */ ); ivas_error IVAS_REND_GetNumAllObjects( diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 4bf358a58..0bbad7269 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1678,7 +1678,7 @@ static ivas_error alignInputDelay( Word64 tmp; inputIsm = (input_ism *) inputBase; - tmp = W_mult0_32_32( L_add( maxGlobalDelayNs, 500000 ) /* + 0.5ms for rounding */, /* Q0 */, 2147 /* (1 / 1e6f) in Q31 */ ); // Q31 + tmp = W_mult0_32_32( L_add( maxGlobalDelayNs, 500000 /* + 0.5ms for rounding */ ) /* Q0 */, 2147 /* (1 / 1e6f) in Q31 */ ); // Q31 inputIsm->ism_metadata_delay_ms_fx = add( inputIsm->ism_metadata_delay_ms_fx, extract_h( W_extract_h( tmp ) ) ); } } @@ -4486,7 +4486,7 @@ ivas_error IVAS_REND_AddInput_fx( } #ifdef CODE_IMPROVEMENTS - setInputDelay( getInputByIndex( inputsArray, inputIndex, inputType ), splitPreRendCldfb ); + setInputDelay( getInputByIndex_fx( inputsArray, inputIndex, inputType ), splitPreRendCldfb ); #else setInputDelay( (Word8 *) inputsArray + inputStructSize * inputIndex, splitPreRendCldfb ); #endif -- GitLab From 8676830aa68255ea32d0d2938130d46663b37aac Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Wed, 29 Oct 2025 18:47:30 +0100 Subject: [PATCH 08/36] align data type between field and function returns --- lib_rend/lib_rend_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 0bbad7269..0234fc6f5 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -114,7 +114,7 @@ typedef struct Word32 gain_fx; /* Linear, not in dB Q30 */ rendering_context ctx; Word32 numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */ - Word32 delayNumSamples; + Word16 delayNumSamples; } input_base; typedef struct -- GitLab From e0f293c617571a41428bf055a374a8f49be26e6c Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 15:52:21 +0100 Subject: [PATCH 09/36] port data type changes from float branch --- lib_rend/ivas_prot_rend_fx.h | 10 +++++----- lib_rend/ivas_td_ring_buffer_fx.c | 31 ++++++++++++++----------------- lib_rend/lib_rend_fx.c | 9 ++++----- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/lib_rend/ivas_prot_rend_fx.h b/lib_rend/ivas_prot_rend_fx.h index 54d6d10fa..783798f0d 100644 --- a/lib_rend/ivas_prot_rend_fx.h +++ b/lib_rend/ivas_prot_rend_fx.h @@ -1601,7 +1601,7 @@ void ivas_rend_closeCldfbRend( ivas_error ivas_TD_RINGBUF_Open( TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ - const UWord32 capacity_per_channel, /* i : Number of samples stored per channel */ + const UWord16 capacity_per_channel, /* i : Number of samples stored per channel */ const UWord16 num_channels /* i : Number of channels */ ); @@ -1612,21 +1612,21 @@ void ivas_TD_RINGBUF_Close( void ivas_TD_RINGBUF_Push( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ const Word32 *data, /* i : Input data */ - const UWord32 num_samples_per_channel /* i : Number of samples per channel to store */ + const UWord16 num_samples_per_channel /* i : Number of samples per channel to store */ ); void ivas_TD_RINGBUF_PushZeros( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - const UWord32 num_samples_per_channel /* i : Number of zeros per channel to store */ + const UWord16 num_samples_per_channel /* i : Number of zeros per channel to store */ ); void ivas_TD_RINGBUF_Pop( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ Word32 *data, /* i : Output data */ - const UWord32 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ + const UWord16 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ ); -uint32_t ivas_TD_RINGBUF_Size( +uint16_t ivas_TD_RINGBUF_Size( const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ ); diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 6e34ab31c..024208e0c 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -105,14 +105,14 @@ static Word16 ivas_td_ringbuf_has_space_for_num_samples( ivas_error ivas_TD_RINGBUF_Open( TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ - const UWord32 capacity_per_channel, /* i : Number of samples stored per channel */ + const UWord16 capacity_per_channel, /* i : Number of samples stored per channel */ const UWord16 num_channels /* i : Number of channels */ ) { TD_RINGBUF_HANDLE h; UWord32 capacity; - capacity = Mult_32_16( capacity_per_channel, num_channels ); + capacity = L_mult0( capacity_per_channel, num_channels ); // Q0 x Q0 -> Q0 h = malloc( sizeof( TD_RINGBUF_DATA ) ); IF( h == NULL ) @@ -197,13 +197,12 @@ void ivas_TD_RINGBUF_Close( void ivas_TD_RINGBUF_Push( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ const Word32 *data, /* i : Input data */ - const UWord32 num_samples_per_channel /* i : Number of samples per channel to store */ + const UWord16 num_samples_per_channel /* i : Number of samples per channel to store */ ) { - UWord32 s; - UWord16 c; + UWord16 c, s; - assert( ivas_td_ringbuf_has_space_for_num_samples( h, Mult_32_16( num_samples_per_channel, h->num_channels ) ) ); + assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); FOR( s = 0; s < num_samples_per_channel; ++s ) { @@ -245,14 +244,13 @@ void ivas_TD_RINGBUF_Push( void ivas_TD_RINGBUF_PushZeros( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - const UWord32 num_samples_per_channel /* i : Number of zeros per channel to store */ + const UWord16 num_samples_per_channel /* i : Number of zeros per channel to store */ ) { - UWord32 s; - UWord16 c; + UWord16 c, s; - assert( ivas_td_ringbuf_has_space_for_num_samples( h, Mult_32_16( num_samples_per_channel, h->num_channels ) ) ); - IF( EQ_32( num_samples_per_channel, 0 ) ) + assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + IF( EQ_16( num_samples_per_channel, 0 ) ) { return; } @@ -292,14 +290,13 @@ void ivas_TD_RINGBUF_PushZeros( void ivas_TD_RINGBUF_Pop( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ Word32 *data, /* i : Output data */ - const UWord32 num_samples_per_channel /* i : Number of samples per channel to retrieve */ + const UWord16 num_samples_per_channel /* i : Number of samples per channel to retrieve */ ) { - UWord32 s; - UWord16 c; + UWord16 c, s; Word32 requested_samples; - requested_samples = Mult_32_16( num_samples_per_channel, h->num_channels ); + requested_samples = L_mult0( num_samples_per_channel, h->num_channels ); assert( GE_32( ivas_td_ringbuf_total_size( h ), requested_samples ) ); FOR( s = 0; s < num_samples_per_channel; ++s ) @@ -339,11 +336,11 @@ void ivas_TD_RINGBUF_Pop( * Returns number of buffered samples per channel. *---------------------------------------------------------------------*/ -UWord32 ivas_TD_RINGBUF_Size( +UWord16 ivas_TD_RINGBUF_Size( const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ ) { - UWord32 size; + UWord16 size; size = div_l( ivas_td_ringbuf_total_size( h ), h->num_channels ); return size; diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 0234fc6f5..f9c900897 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1642,9 +1642,8 @@ static ivas_error alignInputDelay( { ivas_error error; input_ism *inputIsm; - Word16 maxGlobalDelaySamples; - Word32 numSamplesToPush, numSamplesToPop; - UWord32 ringBufferSize, preDelay; + Word16 maxGlobalDelaySamples, numSamplesToPush, numSamplesToPop; + UWord16 ringBufferSize, preDelay; maxGlobalDelaySamples = latencyNsToSamples( sampleRateOut, maxGlobalDelayNs ); maxGlobalDelaySamples = i_mult( maxGlobalDelaySamples, cldfb2tdSampleFact ); @@ -1691,7 +1690,7 @@ static ivas_error alignInputDelay( IF( NE_16( flushInputs, 0 ) ) { numSamplesToPush = 0; - move32(); + move16(); numSamplesToPop = ivas_TD_RINGBUF_Size( inputBase->delayBuffer ); } @@ -1700,7 +1699,7 @@ static ivas_error alignInputDelay( numSamplesToPush = inputAudio.config.numSamplesPerChannel; move32(); - numSamplesToPop = (Word32) inputAudio.config.numSamplesPerChannel; + numSamplesToPop = inputAudio.config.numSamplesPerChannel; move32(); } -- GitLab From 52404c50b0ccacf1bdba6d198a95a0293ae0ffee Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 16:06:27 +0100 Subject: [PATCH 10/36] fix build with DEBUGGING active --- lib_rend/lib_rend_fx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index f9c900897..cbec31651 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -233,6 +233,10 @@ struct IVAS_REND Word8 rendererConfigEnabled; RENDER_CONFIG_DATA *hRendererConfig; /* Renderer config pointer */ +#ifdef DEBUGGING + Word32 numClipping; /* Counter of clipped output samples */ +#endif + Word16 num_subframes; hrtf_handles hHrtfs; }; -- GitLab From 08217b00fccb8b3cb3ab2cfa105d2d141e4cfea6 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 16:17:36 +0100 Subject: [PATCH 11/36] fix division --- lib_rend/lib_rend_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index cbec31651..a61a321cc 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9326,7 +9326,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( /* Pose correction requires 20ms */ ELSE { - pSplitEncBufConfig->numSamplesPerChannel = div_s( hIvasRend->sampleRateOut, FRAMES_PER_SEC ); + pSplitEncBufConfig->numSamplesPerChannel = shl( div_l( hIvasRend->sampleRateOut, FRAMES_PER_SEC ), 1 ); } /* Note: float was "pSplitEncBufConfig->numSamplesPerChannel *= cldfb_in_flag ? 2 : 1;" */ -- GitLab From 01ec33995e7b21fc8020b7d0f94e1277b8ceecc5 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 16:57:07 +0100 Subject: [PATCH 12/36] fix misplaced code whcih caused segfault --- lib_rend/lib_rend_fx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index a61a321cc..06ced5168 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -4407,10 +4407,11 @@ ivas_error IVAS_REND_AddInput_fx( { return error; } + + /*assumes that input has been added which means codec has been set to either lcld or lc3plus (even if render config specified default)*/ + splitPreRendCldfb = EQ_32( hIvasRend->hRendererConfig->split_rend_config.codec, ISAR_SPLIT_REND_CODEC_LCLD ); } - /*assumes that input has been added which means codec has been set to either lcld or lc3plus (even if render config specified default)*/ - splitPreRendCldfb = EQ_32( hIvasRend->hRendererConfig->split_rend_config.codec, ISAR_SPLIT_REND_CODEC_LCLD ); #ifdef CODE_IMPROVEMENTS inputType = getAudioConfigType( inConfig ); -- GitLab From 843c8462eb299320fc6a083c6d2979c93172c13b Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 17:10:24 +0100 Subject: [PATCH 13/36] remove spurious assert --- lib_rend/lib_rend_fx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 06ced5168..2ad411fcc 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -4868,13 +4868,11 @@ ivas_error IVAS_REND_GetDelay_fx( return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - assert( *timeScale == 8000 || *timeScale == 16000 || *timeScale == 32000 || *timeScale == 48000 ); - *nSamples = 0; move16(); *timeScale = hIvasRend->sampleRateOut; - + move32(); max_latency_ns = getMaxGlobalDelayNs( hIvasRend ); *nSamples = latencyNsToSamples( hIvasRend->sampleRateOut, max_latency_ns ); -- GitLab From 3056cb80b81222880b80eff705e1d46f0f947646 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 17:57:29 +0100 Subject: [PATCH 14/36] fix pre delay calculation + add some missing move's --- lib_rend/lib_rend_fx.c | 47 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 2ad411fcc..910c95654 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1660,7 +1660,6 @@ static ivas_error alignInputDelay( shl( inputAudio.config.numSamplesPerChannel, 1 ) ); /* pre delay for this input is maximum delay - input delay */ - preDelay = maxGlobalDelaySamples - inputBase->delayNumSamples * cldfb2tdSampleFact; preDelay = sub( maxGlobalDelaySamples, i_mult( inputBase->delayNumSamples, cldfb2tdSampleFact ) ); @@ -1701,10 +1700,10 @@ static ivas_error alignInputDelay( ELSE { numSamplesToPush = inputAudio.config.numSamplesPerChannel; - move32(); + move16(); numSamplesToPop = inputAudio.config.numSamplesPerChannel; - move32(); + move16(); } ivas_TD_RINGBUF_Push( inputBase->delayBuffer, inputAudio.data_fx, numSamplesToPush ); @@ -3607,13 +3606,20 @@ ivas_error IVAS_REND_Open( } hIvasRend = *phIvasRend; + move32(); hIvasRend->sampleRateOut = outputSampleRate; + move32(); hIvasRend->outputConfig = outConfig; + move32(); hIvasRend->customLsOut = defaultCustomLs(); hIvasRend->hLimiter = NULL; + move32(); hIvasRend->efapOutWrapper.hEfap = NULL; + move32(); hIvasRend->efapOutWrapper.pCustomLsSetup = NULL; + move32(); hIvasRend->num_subframes = num_subframes; + move16(); /* Initialize limiter */ IF( NE_32( ( error = IVAS_REND_GetNumOutChannels( hIvasRend, &numOutChannels ) ), IVAS_ERR_OK ) ) @@ -3675,27 +3681,38 @@ ivas_error IVAS_REND_Open( isar_init_split_rend_handles( hIvasRend->splitRendWrapper ); } hIvasRend->splitRendEncBuffer.data_fx = NULL; + move32(); hIvasRend->splitRendEncBuffer.config.is_cldfb = 0; + move16(); hIvasRend->splitRendEncBuffer.config.numChannels = 0; + move16(); hIvasRend->splitRendEncBuffer.config.numSamplesPerChannel = 0; + move16(); FOR( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) { initRendInputBase_fx( &hIvasRend->inputsIsm[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); hIvasRend->inputsIsm[i].crendWrapper = NULL; + move32(); hIvasRend->inputsIsm[i].hReverb = NULL; + move32(); hIvasRend->inputsIsm[i].tdRendWrapper.hBinRendererTd = NULL; + move32(); FOR( j = 0; j < MAX_HEAD_ROT_POSES - 1; ++j ) { hIvasRend->inputsIsm[i].splitTdRendWrappers[j].hBinRendererTd = NULL; + move32(); hIvasRend->inputsIsm[i].splitTdRendWrappers[j].hHrtfTD = NULL; + move32(); } hIvasRend->inputsIsm[i].nonDiegeticPan = nonDiegeticPan; move16(); hIvasRend->inputsIsm[i].nonDiegeticPanGain_fx = nonDiegeticPanGain; move32(); hIvasRend->inputsIsm[i].hOMasa = NULL; + move32(); hIvasRend->inputsIsm[i].bufferData_fx = NULL; + move32(); } FOR( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) @@ -3703,20 +3720,29 @@ ivas_error IVAS_REND_Open( initRendInputBase_fx( &hIvasRend->inputsMc[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); hIvasRend->inputsMc[i].efapInWrapper.hEfap = NULL; + move32(); hIvasRend->inputsMc[i].crendWrapper = NULL; + move32(); hIvasRend->inputsMc[i].hReverb = NULL; + move32(); hIvasRend->inputsMc[i].tdRendWrapper.hBinRendererTd = NULL; + move32(); hIvasRend->inputsMc[i].bufferData_fx = NULL; + move32(); hIvasRend->inputsMc[i].lfeDelayBuffer_fx = NULL; + move32(); hIvasRend->inputsMc[i].nonDiegeticPan = nonDiegeticPan; move16(); hIvasRend->inputsMc[i].nonDiegeticPanGain_fx = nonDiegeticPanGain; move32(); hIvasRend->inputsMc[i].hMcMasa = NULL; + move32(); FOR( j = 0; j < MAX_HEAD_ROT_POSES - 1; ++j ) { hIvasRend->inputsMc[i].splitTdRendWrappers[j].hBinRendererTd = NULL; + move32(); hIvasRend->inputsMc[i].splitTdRendWrappers[j].hHrtfTD = NULL; + move32(); } } @@ -3725,10 +3751,15 @@ ivas_error IVAS_REND_Open( initRendInputBase_fx( &hIvasRend->inputsSba[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); hIvasRend->inputsSba[i].crendWrapper = NULL; + move32(); hIvasRend->inputsSba[i].cldfbRendWrapper.hCldfbRend = NULL; + move32(); hIvasRend->inputsSba[i].cldfbRendWrapper.hHrtfFastConv = NULL; + move32(); hIvasRend->inputsSba[i].bufferData_fx = NULL; + move32(); hIvasRend->inputsSba[i].hDirAC = NULL; + move32(); } FOR( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) @@ -3736,17 +3767,25 @@ ivas_error IVAS_REND_Open( initRendInputBase_fx( &hIvasRend->inputsMasa[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); hIvasRend->inputsMasa[i].metadataHasBeenFed = false; + move16(); hIvasRend->inputsMasa[i].bufferData_fx = NULL; + move32(); hIvasRend->inputsMasa[i].hMasaPrerend = NULL; + move32(); hIvasRend->inputsMasa[i].hMasaExtRend = NULL; - move16(); + move32(); } hIvasRend->hHrtfs.hHrtfFastConv = NULL; + move32(); hIvasRend->hHrtfs.hHrtfParambin = NULL; + move32(); hIvasRend->hHrtfs.hHrtfTD = NULL; + move32(); hIvasRend->hHrtfs.hHrtfCrend = NULL; + move32(); hIvasRend->hHrtfs.hHrtfStatistics = NULL; + move32(); IF( asHrtfBinary ) { -- GitLab From 725f34872403eacec3ff8b6f8a4948c3924d3e39 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 18:26:49 +0100 Subject: [PATCH 15/36] fix td ring buffer total size function port went wrong somehow --- lib_rend/ivas_td_ring_buffer_fx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 024208e0c..c90b7e15f 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -53,21 +53,17 @@ static UWord32 ivas_td_ringbuf_total_size( TD_RINGBUF_HANDLE h ) { - UWord32 total_size; - IF( h->is_full ) { - total_size = h->capacity; + return h->capacity; } IF( h->read_pos <= h->write_pos ) { - total_size = L_sub( h->write_pos, h->read_pos ); + return L_sub( h->write_pos, h->read_pos ); } /* else wrap around */ - total_size = L_add( h->write_pos, L_sub( h->capacity, h->read_pos ) ); - - return total_size; + return L_add( h->write_pos, L_sub( h->capacity, h->read_pos ) ); } -- GitLab From 9e6fcf0636dd0a0521524596a5ff6e9e366e2a01 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 18:45:44 +0100 Subject: [PATCH 16/36] use constant instead of magic number --- lib_rend/lib_rend_fx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 910c95654..3975b046c 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -6273,7 +6273,7 @@ static ivas_error renderIsmToBinaural( push_wmops( "renderIsmToBinaural" ); /* Metadata Delay to sync with audio delay converted from ms to 5ms (1000/50/4) subframe index */ - ism_md_subframe_update_ext = extract_l( Mpy_32_32( ismInput->ism_metadata_delay_ms_fx, 429496730 /* 1000 / FRAMES_PER_SEC / MAX_PARAM_SPATIAL_SUBFRAMES in Q31 */ ) ); + ism_md_subframe_update_ext = extract_l( Mpy_32_32( ismInput->ism_metadata_delay_ms_fx, ONE_BY_SUBFRAME_LEN_MS_Q31 ) ); copyBufferTo2dArray_fx( ismInput->base.inputBuffer, tmpTDRendBuffer ); FOR( i = 0; i < MAX_OUTPUT_CHANNELS; ++i ) @@ -6543,7 +6543,7 @@ static ivas_error renderIsmToBinauralReverb( push_wmops( "renderIsmToBinauralRoom" ); /* Metadata Delay to sync with audio delay converted from ms to 5ms (1000/50/4) subframe index */ - ism_md_subframe_update_ext = extract_l( Mpy_32_32( ismInput->ism_metadata_delay_ms_fx, 429496730 /* 1000 / FRAMES_PER_SEC / MAX_PARAM_SPATIAL_SUBFRAMES in Q31 */ ) ); + ism_md_subframe_update_ext = extract_l( Mpy_32_32( ismInput->ism_metadata_delay_ms_fx, ONE_BY_SUBFRAME_LEN_MS_Q31 ) ); copyBufferTo2dArray_fx( ismInput->base.inputBuffer, tmpRendBuffer_fx ); FOR( i = 0; i < MAX_OUTPUT_CHANNELS; ++i ) -- GitLab From d871f85996272c4fdceb52d8e6895fe52521edd8 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Thu, 30 Oct 2025 18:47:58 +0100 Subject: [PATCH 17/36] fix logic error --- lib_rend/lib_rend_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 3975b046c..6853acb27 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9389,7 +9389,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( } /* copy outputs */ - IF( NE_16( hIvasRend->splitRendEncBuffer.config.is_cldfb, 1 ) ) + IF( EQ_16( hIvasRend->splitRendEncBuffer.config.is_cldfb, 1 ) ) { cldfb_in_flag = 1; move16(); -- GitLab From faa2953f82a06cdef0402c05890e56ce55197999 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 09:51:13 +0100 Subject: [PATCH 18/36] align with parallel port for later MR --- lib_rend/ivas_prot_rend_fx.h | 16 ++-- lib_rend/ivas_td_ring_buffer_fx.c | 140 ++++++++++++------------------ 2 files changed, 63 insertions(+), 93 deletions(-) diff --git a/lib_rend/ivas_prot_rend_fx.h b/lib_rend/ivas_prot_rend_fx.h index 783798f0d..84dcc6072 100644 --- a/lib_rend/ivas_prot_rend_fx.h +++ b/lib_rend/ivas_prot_rend_fx.h @@ -33,7 +33,6 @@ #ifndef IVAS_PROT_REND_H #define IVAS_PROT_REND_H -#include "typedef.h" #include #include "options.h" #include "ivas_error.h" @@ -1601,8 +1600,8 @@ void ivas_rend_closeCldfbRend( ivas_error ivas_TD_RINGBUF_Open( TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ - const UWord16 capacity_per_channel, /* i : Number of samples stored per channel */ - const UWord16 num_channels /* i : Number of channels */ + const Word16 capacity_per_channel, /* i : Number of samples stored per channel */ + const Word16 num_channels /* i : Number of channels */ ); void ivas_TD_RINGBUF_Close( @@ -1612,24 +1611,23 @@ void ivas_TD_RINGBUF_Close( void ivas_TD_RINGBUF_Push( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ const Word32 *data, /* i : Input data */ - const UWord16 num_samples_per_channel /* i : Number of samples per channel to store */ + const Word16 num_samples_per_channel /* i : Number of samples per channel to store */ ); void ivas_TD_RINGBUF_PushZeros( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - const UWord16 num_samples_per_channel /* i : Number of zeros per channel to store */ + const Word16 num_samples_per_channel /* i : Number of zeros per channel to store */ ); void ivas_TD_RINGBUF_Pop( TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ Word32 *data, /* i : Output data */ - const UWord16 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ + const Word16 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ ); +#endif -uint16_t ivas_TD_RINGBUF_Size( +Word16 ivas_TD_RINGBUF_Size( const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ ); /* clang-format on */ - -#endif /* IVAS_PROT_REND_H */ diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index c90b7e15f..2492d30b4 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -30,14 +30,13 @@ *******************************************************************************************************/ -#include "basop32.h" -#include "move.h" -#include "typedef.h" #include #include #include "ivas_error_utils.h" #include "ivas_prot_rend_fx.h" +#include "options.h" #include "wmc_auto.h" +#include "prot_fx.h" /*-----------------------------------------------------------------------* @@ -50,7 +49,7 @@ * Returns total number of buffered samples (including number of channels) *---------------------------------------------------------------------*/ -static UWord32 ivas_td_ringbuf_total_size( +static Word32 ivas_td_ringbuf_total_size( TD_RINGBUF_HANDLE h ) { IF( h->is_full ) @@ -58,7 +57,7 @@ static UWord32 ivas_td_ringbuf_total_size( return h->capacity; } - IF( h->read_pos <= h->write_pos ) + IF( LE_32( h->read_pos, h->write_pos ) ) { return L_sub( h->write_pos, h->read_pos ); } @@ -69,21 +68,13 @@ static UWord32 ivas_td_ringbuf_total_size( static Word16 ivas_td_ringbuf_has_space_for_num_samples( TD_RINGBUF_HANDLE h, - const UWord32 num_samples ) + const Word32 num_samples ) { - // float was : return (Word16) ( ivas_td_ringbuf_total_size( h ) + num_samples <= h->capacity ); - Word16 has_space; - UWord32 total_size_after; - - move16(); - has_space = 0; - - total_size_after = L_add( ivas_td_ringbuf_total_size( h ), num_samples ); - IF( LE_32( total_size_after, h->capacity ) ) + if ( LE_32( L_add( ivas_td_ringbuf_total_size( h ), num_samples ), h->capacity ) ) { - has_space = 1; + return 1; } - return has_space; + return 0; } @@ -100,45 +91,42 @@ static Word16 ivas_td_ringbuf_has_space_for_num_samples( *---------------------------------------------------------------------*/ ivas_error ivas_TD_RINGBUF_Open( - TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ - const UWord16 capacity_per_channel, /* i : Number of samples stored per channel */ - const UWord16 num_channels /* i : Number of channels */ + TD_RINGBUF_HANDLE *ph, /* i/o: Ring buffer handle */ + const Word16 capacity_per_channel, /* i : Number of samples stored per channel */ + const Word16 num_channels /* i : Number of channels */ ) { TD_RINGBUF_HANDLE h; - UWord32 capacity; + Word32 capacity; - capacity = L_mult0( capacity_per_channel, num_channels ); // Q0 x Q0 -> Q0 + capacity = L_mult0( capacity_per_channel, num_channels ); + move32(); h = malloc( sizeof( TD_RINGBUF_DATA ) ); IF( h == NULL ) { return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } - - move32(); h->data = NULL; - move32(); h->capacity = 0; - move16(); - h->num_channels = num_channels; move32(); + h->num_channels = num_channels; + move16(); h->write_pos = 0; move32(); h->read_pos = 0; - move16(); - h->is_full = 0; move32(); + h->is_full = 0; + move16(); *ph = h; h->data = malloc( capacity * sizeof( Word32 ) ); - IF( h->data == NULL ) + if ( h->data == NULL ) { return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } - - move32(); h->capacity = capacity; + move32(); return IVAS_ERR_OK; } @@ -161,8 +149,6 @@ void ivas_TD_RINGBUF_Close( { return; } - - move32(); h = *ph; IF( h == NULL ) @@ -177,7 +163,6 @@ void ivas_TD_RINGBUF_Close( free( h ); *ph = NULL; - move32(); return; } @@ -191,41 +176,36 @@ void ivas_TD_RINGBUF_Close( *---------------------------------------------------------------------*/ void ivas_TD_RINGBUF_Push( - TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - const Word32 *data, /* i : Input data */ - const UWord16 num_samples_per_channel /* i : Number of samples per channel to store */ + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const Word32 *data, /* i : Input data */ + const Word16 num_samples_per_channel /* i : Number of samples per channel to store */ ) { - UWord16 c, s; + Word32 s; + Word16 c; - assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); + assert( ivas_td_ringbuf_has_space_for_num_samples( h, num_samples_per_channel * h->num_channels ) ); FOR( s = 0; s < num_samples_per_channel; ++s ) { FOR( c = 0; c < h->num_channels; ++c ) { - UWord32 shift_val; - - /* pointer is advance by c * num_samples_per_channel + s */ - shift_val = L_mult0( c, num_samples_per_channel ); - shift_val = L_add( s, shift_val ); - + h->data[h->write_pos] = data[L_add( L_mult0( c, num_samples_per_channel ), s )]; move32(); - h->data[h->write_pos] = *( data + shift_val ); ++h->write_pos; - IF( EQ_32( h->write_pos, h->capacity ) ) + if ( EQ_32( h->write_pos, h->capacity ) ) { - move32(); h->write_pos = 0; + move32(); } } } - IF( EQ_32( h->read_pos, h->write_pos ) ) + if ( EQ_32( h->read_pos, h->write_pos ) ) { - move32(); h->is_full = 1; + move16(); } return; @@ -239,14 +219,15 @@ void ivas_TD_RINGBUF_Push( *---------------------------------------------------------------------*/ void ivas_TD_RINGBUF_PushZeros( - TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - const UWord16 num_samples_per_channel /* i : Number of zeros per channel to store */ + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + const Word16 num_samples_per_channel /* i : Number of zeros per channel to store */ ) { - UWord16 c, s; + Word32 s; + Word16 c; - assert( ivas_td_ringbuf_has_space_for_num_samples( h, L_mult0( num_samples_per_channel, h->num_channels ) ) ); - IF( EQ_16( num_samples_per_channel, 0 ) ) + assert( ivas_td_ringbuf_has_space_for_num_samples( h, num_samples_per_channel * h->num_channels ) ); + if ( !num_samples_per_channel ) { return; } @@ -255,22 +236,22 @@ void ivas_TD_RINGBUF_PushZeros( { FOR( c = 0; c < h->num_channels; ++c ) { + h->data[h->write_pos] = 0.f; move32(); - h->data[h->write_pos] = 0; ++h->write_pos; - IF( EQ_32( h->write_pos, h->capacity ) ) + if ( EQ_32( h->write_pos, h->capacity ) ) { - move32(); h->write_pos = 0; + move32(); } } } - IF( EQ_32( h->read_pos, h->write_pos ) ) + if ( EQ_32( h->read_pos, h->write_pos ) ) { - move32(); h->is_full = 1; + move16(); } return; @@ -284,42 +265,36 @@ void ivas_TD_RINGBUF_PushZeros( *---------------------------------------------------------------------*/ void ivas_TD_RINGBUF_Pop( - TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ - Word32 *data, /* i : Output data */ - const UWord16 num_samples_per_channel /* i : Number of samples per channel to retrieve */ + TD_RINGBUF_HANDLE h, /* i/o: Ring buffer handle */ + Word32 *data, /* i : Output data */ + const Word16 num_samples_per_channel /* i : Number of samples per channel to retrieve */ ) { - UWord16 c, s; - Word32 requested_samples; + Word32 s; + Word16 c; - requested_samples = L_mult0( num_samples_per_channel, h->num_channels ); - assert( GE_32( ivas_td_ringbuf_total_size( h ), requested_samples ) ); + assert( ivas_td_ringbuf_total_size( h ) >= num_samples_per_channel * h->num_channels ); FOR( s = 0; s < num_samples_per_channel; ++s ) { FOR( c = 0; c < h->num_channels; ++c ) { - UWord32 shift_val; - - /* pointer is advanced by c * num_samples_per_channel + s */ - shift_val = L_mult0( c, num_samples_per_channel ); - shift_val = L_add( s, shift_val ); - - *( data + shift_val ) = h->data[h->read_pos]; + data[L_add( L_mult0( c, num_samples_per_channel ), s )] = h->data[h->read_pos]; + move32(); ++h->read_pos; - IF( EQ_32( h->write_pos, h->capacity ) ) + IF( EQ_32( h->read_pos, h->capacity ) ) { + h->read_pos = 0; move32(); - h->write_pos = 0; } } } - IF( NE_16( h->is_full, 0 ) ) + if ( h->is_full ) { - move16(); h->is_full = 0; + move16(); } return; @@ -332,12 +307,9 @@ void ivas_TD_RINGBUF_Pop( * Returns number of buffered samples per channel. *---------------------------------------------------------------------*/ -UWord16 ivas_TD_RINGBUF_Size( +Word16 ivas_TD_RINGBUF_Size( const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ ) { - UWord16 size; - - size = div_l( ivas_td_ringbuf_total_size( h ), h->num_channels ); - return size; + return extract_l( ar_div( ivas_td_ringbuf_total_size( h ), L_deposit_l( h->num_channels ) ) ); } -- GitLab From 9ba3dbdb1f9f1c92729c609b1f49365a0f42c427 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 10:40:02 +0100 Subject: [PATCH 19/36] add missing moves --- lib_rend/ivas_td_ring_buffer_fx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index 2492d30b4..cfb3f3e8d 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -108,6 +108,7 @@ ivas_error ivas_TD_RINGBUF_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } h->data = NULL; + move32(); h->capacity = 0; move32(); h->num_channels = num_channels; @@ -119,6 +120,7 @@ ivas_error ivas_TD_RINGBUF_Open( h->is_full = 0; move16(); *ph = h; + move32(); h->data = malloc( capacity * sizeof( Word32 ) ); if ( h->data == NULL ) @@ -150,6 +152,7 @@ void ivas_TD_RINGBUF_Close( return; } h = *ph; + move32(); IF( h == NULL ) { @@ -163,6 +166,7 @@ void ivas_TD_RINGBUF_Close( free( h ); *ph = NULL; + move32(); return; } -- GitLab From b977e505cac80a67645ab5cd1a56d649d6ea9fda Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 13:07:29 +0100 Subject: [PATCH 20/36] use saturating add to fix crash -equivalent spot in encoder code uses that too -also remove two excessive tests()'s --- lib_rend/ivas_mcmasa_ana_fx.c | 2 +- lib_rend/lib_rend_fx.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib_rend/ivas_mcmasa_ana_fx.c b/lib_rend/ivas_mcmasa_ana_fx.c index dbaf5b7d9..ab41810b8 100644 --- a/lib_rend/ivas_mcmasa_ana_fx.c +++ b/lib_rend/ivas_mcmasa_ana_fx.c @@ -1051,7 +1051,7 @@ void ivas_mcmasa_param_est_ana_fx( cohPanCoh_e = tempCoh2_e; move16(); } - cohPanCoh_fx = L_shl( cohPanCoh_fx, cohPanCoh_e ); /*Q31*/ + cohPanCoh_fx = L_shl_sat( cohPanCoh_fx, cohPanCoh_e ); /*Q31*/ cohPanCoh_e = 0; move16(); lsEnergyRelation_fx = BASOP_Util_Divide3232_Scale( lsEnergy_fx[i2], L_add( lsEnergy_fx[i1], EPSILON_FX ), &lsEnergyRelation_e ); diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 6853acb27..64082179f 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1531,8 +1531,6 @@ static Word32 getRendInputDelayMasa( latency_ns = 0; move32(); - test(); - test(); test(); test(); IF( ( EQ_32( inputMasa->base.inConfig, IVAS_AUDIO_CONFIG_MASA1 ) && -- GitLab From 6ced9a2ab94083a8808cf459f2282f72b03e7699 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 13:43:56 +0100 Subject: [PATCH 21/36] fix data type of constant --- lib_rend/ivas_td_ring_buffer_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index cfb3f3e8d..e640f9501 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -240,7 +240,7 @@ void ivas_TD_RINGBUF_PushZeros( { FOR( c = 0; c < h->num_channels; ++c ) { - h->data[h->write_pos] = 0.f; + h->data[h->write_pos] = 0; move32(); ++h->write_pos; -- GitLab From 59aa4423a30ca2c3295b86b18b79494b06b924e4 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 17:18:26 +0100 Subject: [PATCH 22/36] set q factor of splitRendEncBuffer from outAudio buffer --- lib_rend/lib_rend_fx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 64082179f..e537eb71c 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9349,6 +9349,11 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( pSplitEncBufConfig = &hIvasRend->splitRendEncBuffer.config; pSplitRendConfig = &hIvasRend->hRendererConfig->split_rend_config; + *hIvasRend->splitRendEncBuffer.pq_fact = *outAudio.pq_fact; + move16(); + hIvasRend->splitRendEncBuffer.q_factor = outAudio.q_factor; + move16(); + /* 0 DoF / No pose correction retains frame size */ pSplitEncBufConfig->is_cldfb = cldfb_in_flag; move16(); -- GitLab From 2e39f6ded4365f4df62853d6b18fb48fcf712b72 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 17:34:11 +0100 Subject: [PATCH 23/36] saturating sub to fix crash in final renderer pseudo frame --- lib_isar/isar_lcld_encoder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_isar/isar_lcld_encoder.c b/lib_isar/isar_lcld_encoder.c index 915e2a5e4..927932c1b 100644 --- a/lib_isar/isar_lcld_encoder.c +++ b/lib_isar/isar_lcld_encoder.c @@ -1426,7 +1426,7 @@ static void QuantizeSpectrumDPCM_Opt( ppiQImag[iBlockOffset][iFBOffset] = L_shr( ppiQImag_fx, 21 ); ppiQReal_fx = L_shl( ppiQReal[iBlockOffset][iFBOffset], 21 ); - fPrevReal_fx = L_sub( L_shl( UnQuantize_fx( ppiQReal_fx, + fPrevReal_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQReal_fx, fInvSCFGain_fx, ppiSignReal[iBlockOffset][iFBOffset] ), 9 ), @@ -1434,7 +1434,7 @@ static void QuantizeSpectrumDPCM_Opt( /* add prediction to quantized residual = reconstructed sample */ ppiQImag_fx = L_shl( ppiQImag[iBlockOffset][iFBOffset], 21 ); - fPrevImag_fx = L_sub( L_shl( UnQuantize_fx( ppiQImag_fx, + fPrevImag_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQImag_fx, fInvSCFGain_fx, ppiSignImag[iBlockOffset][iFBOffset] ), 9 ), -- GitLab From 8f7b175b0b224483f1f169275dce1e0e92293049 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 17:45:07 +0100 Subject: [PATCH 24/36] clang-format --- lib_isar/isar_lcld_encoder.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib_isar/isar_lcld_encoder.c b/lib_isar/isar_lcld_encoder.c index 927932c1b..16e370c64 100644 --- a/lib_isar/isar_lcld_encoder.c +++ b/lib_isar/isar_lcld_encoder.c @@ -1427,18 +1427,18 @@ static void QuantizeSpectrumDPCM_Opt( ppiQReal_fx = L_shl( ppiQReal[iBlockOffset][iFBOffset], 21 ); fPrevReal_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQReal_fx, - fInvSCFGain_fx, - ppiSignReal[iBlockOffset][iFBOffset] ), - 9 ), - fReal_fx ); + fInvSCFGain_fx, + ppiSignReal[iBlockOffset][iFBOffset] ), + 9 ), + fReal_fx ); /* add prediction to quantized residual = reconstructed sample */ ppiQImag_fx = L_shl( ppiQImag[iBlockOffset][iFBOffset], 21 ); fPrevImag_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQImag_fx, - fInvSCFGain_fx, - ppiSignImag[iBlockOffset][iFBOffset] ), - 9 ), - fImag_fx ); + fInvSCFGain_fx, + ppiSignImag[iBlockOffset][iFBOffset] ), + 9 ), + fImag_fx ); iBlockOffset++; } /* group length */ } /* groups */ -- GitLab From c405bf59ff9fac8b89f7994bffc9086058e21c05 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 31 Oct 2025 17:57:07 +0100 Subject: [PATCH 25/36] add another saturating operator --- lib_isar/isar_lcld_encoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_isar/isar_lcld_encoder.c b/lib_isar/isar_lcld_encoder.c index 16e370c64..87e4f39cc 100644 --- a/lib_isar/isar_lcld_encoder.c +++ b/lib_isar/isar_lcld_encoder.c @@ -1411,7 +1411,7 @@ static void QuantizeSpectrumDPCM_Opt( FOR( k = 0; k < piGroupLengths[n]; k++ ) { /* prediction */ - fReal_fx = L_sub( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevReal_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevImag_fx ) ); + fReal_fx = L_sub_sat( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevReal_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevImag_fx ) ); fImag_fx = L_add( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevImag_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevReal_fx ) ); ppiQReal_fx = Quantize_fx( L_add_sat( L_shr_r_sat( ppfReal_fx[iBlockOffset][iFBOffset], sub( q_final, Q28 ) ), fReal_fx ), /* quantize residual */ fSCFGain_fx, -- GitLab From aa91e67eebfe27d38afbcae939ea7809825909e4 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 3 Nov 2025 13:41:57 +0100 Subject: [PATCH 26/36] fix inverted logic in if condition that controls limiter being applied --- lib_rend/lib_rend_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index e537eb71c..ff12ac1bd 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9291,7 +9291,7 @@ ivas_error IVAS_REND_GetSamples( } - IF( NE_16( outAudio.config.is_cldfb, 0 ) ) + IF( EQ_16( outAudio.config.is_cldfb, 0 ) ) { Word32 limiter_threshold = L_lshl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); #ifndef DISABLE_LIMITER -- GitLab From e0f58ca142236b273577a2ef30cc66529cad40eb Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 3 Nov 2025 14:48:28 +0100 Subject: [PATCH 27/36] improve precision of latencyNsToSamples NS2SA macro was off by one for low values --- lib_rend/lib_rend_fx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index ff12ac1bd..cd8490928 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -289,10 +289,12 @@ static Word16 latencyNsToSamples( Word32 sampleRate, Word32 latency_ns ) { - Word16 n_samples; + Word16 var1, exp; + Word32 var2; + var1 = BASOP_Util_Divide3232_Scale( sampleRate, 1000000000, &exp ); + var2 = L_shr_r( Mpy_32_32( latency_ns, L_deposit_h( var1 ) ), negate( exp ) ); /* Q0 */ - n_samples = NS2SA_FX2( sampleRate, latency_ns ); - return n_samples; + return extract_l(var2); } static ivas_error allocateMcLfeDelayBuffer_fx( -- GitLab From 1ae952addfdb9915124aa62555b226db72405c33 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 3 Nov 2025 14:52:27 +0100 Subject: [PATCH 28/36] clang-format --- lib_rend/lib_rend_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index cd8490928..770cc89aa 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -294,7 +294,7 @@ static Word16 latencyNsToSamples( var1 = BASOP_Util_Divide3232_Scale( sampleRate, 1000000000, &exp ); var2 = L_shr_r( Mpy_32_32( latency_ns, L_deposit_h( var1 ) ), negate( exp ) ); /* Q0 */ - return extract_l(var2); + return extract_l( var2 ); } static ivas_error allocateMcLfeDelayBuffer_fx( -- GitLab From fd6402296bdbf390f768ca46b66dae0da6c4fcaa Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 3 Nov 2025 15:55:47 +0100 Subject: [PATCH 29/36] align ring buffer code with later MR --- lib_rend/ivas_prot_rend_fx.h | 3 ++- lib_rend/ivas_stat_rend.h | 10 +++++----- lib_rend/ivas_td_ring_buffer_fx.c | 18 +++++++----------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/lib_rend/ivas_prot_rend_fx.h b/lib_rend/ivas_prot_rend_fx.h index 84dcc6072..ef62dbe9d 100644 --- a/lib_rend/ivas_prot_rend_fx.h +++ b/lib_rend/ivas_prot_rend_fx.h @@ -1624,10 +1624,11 @@ void ivas_TD_RINGBUF_Pop( Word32 *data, /* i : Output data */ const Word16 num_samples_per_channel /* i : Number of samples per channel to retrieve*/ ); -#endif Word16 ivas_TD_RINGBUF_Size( const TD_RINGBUF_HANDLE h /* i : Ring buffer handle */ ); /* clang-format on */ + +#endif /* IVAS_PROT_REND_H */ diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 75a37e30f..3f6ac811b 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -1440,11 +1440,11 @@ typedef struct typedef struct { - Word32 *data; /* samples in interleaved layout, e.g. for channels A, B, C, samples are stored: A1, B1, C1, A2, B2, C2, ... */ - UWord32 capacity; /* max number of Word32 values that can be stored */ - UWord16 num_channels; - UWord32 write_pos; - UWord32 read_pos; + Word32 *data; /* samples in interleaved layout, e.g. for channels A, B, C, samples are stored: A1, B1, C1, A2, B2, C2, ... */ + Word32 capacity; /* max number of Word32 values that can be stored */ + Word16 num_channels; + Word32 write_pos; + Word32 read_pos; Word16 is_full; } TD_RINGBUF_DATA, *TD_RINGBUF_HANDLE; diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index e640f9501..a7301a3a6 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -70,7 +70,7 @@ static Word16 ivas_td_ringbuf_has_space_for_num_samples( TD_RINGBUF_HANDLE h, const Word32 num_samples ) { - if ( LE_32( L_add( ivas_td_ringbuf_total_size( h ), num_samples ), h->capacity ) ) + IF( LE_32( L_add( ivas_td_ringbuf_total_size( h ), num_samples ), h->capacity ) ) { return 1; } @@ -108,7 +108,6 @@ ivas_error ivas_TD_RINGBUF_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } h->data = NULL; - move32(); h->capacity = 0; move32(); h->num_channels = num_channels; @@ -120,10 +119,9 @@ ivas_error ivas_TD_RINGBUF_Open( h->is_full = 0; move16(); *ph = h; - move32(); h->data = malloc( capacity * sizeof( Word32 ) ); - if ( h->data == NULL ) + IF( h->data == NULL ) { return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to allocate memory for TD ring buffer\n" ); } @@ -152,7 +150,6 @@ void ivas_TD_RINGBUF_Close( return; } h = *ph; - move32(); IF( h == NULL ) { @@ -166,7 +163,6 @@ void ivas_TD_RINGBUF_Close( free( h ); *ph = NULL; - move32(); return; } @@ -196,7 +192,7 @@ void ivas_TD_RINGBUF_Push( { h->data[h->write_pos] = data[L_add( L_mult0( c, num_samples_per_channel ), s )]; move32(); - ++h->write_pos; + h->write_pos = L_add( h->write_pos, 1 ); if ( EQ_32( h->write_pos, h->capacity ) ) { @@ -231,7 +227,7 @@ void ivas_TD_RINGBUF_PushZeros( Word16 c; assert( ivas_td_ringbuf_has_space_for_num_samples( h, num_samples_per_channel * h->num_channels ) ); - if ( !num_samples_per_channel ) + IF( !num_samples_per_channel ) { return; } @@ -242,9 +238,9 @@ void ivas_TD_RINGBUF_PushZeros( { h->data[h->write_pos] = 0; move32(); - ++h->write_pos; + h->write_pos = L_add( h->write_pos, 1 ); - if ( EQ_32( h->write_pos, h->capacity ) ) + IF( EQ_32( h->write_pos, h->capacity ) ) { h->write_pos = 0; move32(); @@ -295,7 +291,7 @@ void ivas_TD_RINGBUF_Pop( } } - if ( h->is_full ) + IF( h->is_full ) { h->is_full = 0; move16(); -- GitLab From aa36b6b40b14360f3308e0cdf6f24f1c069db727 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 3 Nov 2025 16:07:49 +0100 Subject: [PATCH 30/36] use MAC operators instead of separate add and mult --- lib_rend/ivas_td_ring_buffer_fx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_rend/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c index a7301a3a6..24ab8a8a6 100644 --- a/lib_rend/ivas_td_ring_buffer_fx.c +++ b/lib_rend/ivas_td_ring_buffer_fx.c @@ -190,7 +190,7 @@ void ivas_TD_RINGBUF_Push( { FOR( c = 0; c < h->num_channels; ++c ) { - h->data[h->write_pos] = data[L_add( L_mult0( c, num_samples_per_channel ), s )]; + h->data[h->write_pos] = data[L_mac0( s, c, num_samples_per_channel )]; move32(); h->write_pos = L_add( h->write_pos, 1 ); @@ -279,7 +279,7 @@ void ivas_TD_RINGBUF_Pop( { FOR( c = 0; c < h->num_channels; ++c ) { - data[L_add( L_mult0( c, num_samples_per_channel ), s )] = h->data[h->read_pos]; + data[L_mac0( s, c, num_samples_per_channel )] = h->data[h->read_pos]; move32(); ++h->read_pos; -- GitLab From cb0418136aaaa387dba9bf0fd951048bca026ddd Mon Sep 17 00:00:00 2001 From: rtyag Date: Tue, 4 Nov 2025 15:12:13 +1100 Subject: [PATCH 31/36] fix bugs in lib rend --- lib_isar/isar_lcld_encoder.c | 6 ++--- lib_rend/lib_rend_fx.c | 46 +++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lib_isar/isar_lcld_encoder.c b/lib_isar/isar_lcld_encoder.c index 87e4f39cc..4d3b4f042 100644 --- a/lib_isar/isar_lcld_encoder.c +++ b/lib_isar/isar_lcld_encoder.c @@ -1411,7 +1411,7 @@ static void QuantizeSpectrumDPCM_Opt( FOR( k = 0; k < piGroupLengths[n]; k++ ) { /* prediction */ - fReal_fx = L_sub_sat( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevReal_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevImag_fx ) ); + fReal_fx = L_sub( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevReal_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevImag_fx ) ); fImag_fx = L_add( Mpy_32_32( pfA1Real_fx[iFBOffset], fPrevImag_fx ), Mpy_32_32( pfA1Imag_fx[iFBOffset], fPrevReal_fx ) ); ppiQReal_fx = Quantize_fx( L_add_sat( L_shr_r_sat( ppfReal_fx[iBlockOffset][iFBOffset], sub( q_final, Q28 ) ), fReal_fx ), /* quantize residual */ fSCFGain_fx, @@ -1426,7 +1426,7 @@ static void QuantizeSpectrumDPCM_Opt( ppiQImag[iBlockOffset][iFBOffset] = L_shr( ppiQImag_fx, 21 ); ppiQReal_fx = L_shl( ppiQReal[iBlockOffset][iFBOffset], 21 ); - fPrevReal_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQReal_fx, + fPrevReal_fx = L_sub( L_shl( UnQuantize_fx( ppiQReal_fx, fInvSCFGain_fx, ppiSignReal[iBlockOffset][iFBOffset] ), 9 ), @@ -1434,7 +1434,7 @@ static void QuantizeSpectrumDPCM_Opt( /* add prediction to quantized residual = reconstructed sample */ ppiQImag_fx = L_shl( ppiQImag[iBlockOffset][iFBOffset], 21 ); - fPrevImag_fx = L_sub_sat( L_shl( UnQuantize_fx( ppiQImag_fx, + fPrevImag_fx = L_sub( L_shl( UnQuantize_fx( ppiQImag_fx, fInvSCFGain_fx, ppiSignImag[iBlockOffset][iFBOffset] ), 9 ), diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 770cc89aa..c2e812b76 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1641,7 +1641,7 @@ static ivas_error alignInputDelay( const IVAS_REND_ReadOnlyAudioBuffer inputAudio, const Word32 maxGlobalDelayNs, const Word32 sampleRateOut, - const Word16 cldfb2tdSampleFact, + const Word16 cldfb2tdSampleShift, const bool flushInputs ) { ivas_error error; @@ -1650,7 +1650,7 @@ static ivas_error alignInputDelay( UWord16 ringBufferSize, preDelay; maxGlobalDelaySamples = latencyNsToSamples( sampleRateOut, maxGlobalDelayNs ); - maxGlobalDelaySamples = i_mult( maxGlobalDelaySamples, cldfb2tdSampleFact ); + maxGlobalDelaySamples = shl( maxGlobalDelaySamples, cldfb2tdSampleShift ); /* check if we need to open the delay buffer */ IF( inputBase->delayBuffer == NULL ) @@ -1661,7 +1661,7 @@ static ivas_error alignInputDelay( /* pre delay for this input is maximum delay - input delay */ preDelay = sub( maxGlobalDelaySamples, - i_mult( inputBase->delayNumSamples, cldfb2tdSampleFact ) ); + shl( inputBase->delayNumSamples, cldfb2tdSampleShift ) ); IF( GT_32( preDelay, 0 ) ) { @@ -9422,6 +9422,46 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( } } + Word16 q1 = 31, q2 = 31, Q_buff, j; + Word16 num_poses = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; + + FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; i++ ) + { + FOR( j = 0; j < CLDFB_NO_COL_MAX; j++ ) + { + q1 = s_min( q1, L_norm_arr( Cldfb_RealBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX ) ); + q2 = s_min( q2, L_norm_arr( Cldfb_ImagBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX ) ); + } + } + Q_buff = s_min( q1, q2 ); + FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; i++ ) + { + FOR( j = 0; j < CLDFB_NO_COL_MAX; j++ ) + { + scale_sig32( Cldfb_RealBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX, Q_buff ); + scale_sig32( Cldfb_ImagBuffer_Binaural[i][j], CLDFB_NO_CHANNELS_MAX, Q_buff ); + } + } + Q_buff = Q_buff + *outAudio.pq_fact; + + IF( EQ_16( cldfb_in_flag, 0 ) ) + { + /*TD input*/ + num_poses = hIvasRend->splitRendWrapper->multiBinPoseData.num_poses; + + FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; ++i ) + { + Q_out[0] = s_min( Q_out[0], L_norm_arr( tmpBinaural_buff[i], L_FRAME48k ) ); + } + + FOR( i = 0; i < num_poses * BINAURAL_CHANNELS; ++i ) + { + scale_sig32( tmpBinaural_buff[i], L_FRAME48k, Q_out[0] ); + } + + Q_out[0] = Q_out[0] + *outAudio.pq_fact; + } + max_bands = extract_l( Mpy_32_32( imult3216( hIvasRend->sampleRateOut, BINAURAL_MAXBANDS ), 44740 /* 1/48000 in Q31 */ ) ); IF( ( error = ISAR_PRE_REND_MultiBinToSplitBinaural( hIvasRend->splitRendWrapper, hIvasRend->headRotData.headPositions[0], -- GitLab From 9be0431adafead8a3f4c27b0cfaab954306590cc Mon Sep 17 00:00:00 2001 From: rtyag Date: Tue, 4 Nov 2025 15:15:13 +1100 Subject: [PATCH 32/36] clang format fix --- lib_isar/isar_lcld_encoder.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib_isar/isar_lcld_encoder.c b/lib_isar/isar_lcld_encoder.c index 4d3b4f042..915e2a5e4 100644 --- a/lib_isar/isar_lcld_encoder.c +++ b/lib_isar/isar_lcld_encoder.c @@ -1427,18 +1427,18 @@ static void QuantizeSpectrumDPCM_Opt( ppiQReal_fx = L_shl( ppiQReal[iBlockOffset][iFBOffset], 21 ); fPrevReal_fx = L_sub( L_shl( UnQuantize_fx( ppiQReal_fx, - fInvSCFGain_fx, - ppiSignReal[iBlockOffset][iFBOffset] ), - 9 ), - fReal_fx ); + fInvSCFGain_fx, + ppiSignReal[iBlockOffset][iFBOffset] ), + 9 ), + fReal_fx ); /* add prediction to quantized residual = reconstructed sample */ ppiQImag_fx = L_shl( ppiQImag[iBlockOffset][iFBOffset], 21 ); fPrevImag_fx = L_sub( L_shl( UnQuantize_fx( ppiQImag_fx, - fInvSCFGain_fx, - ppiSignImag[iBlockOffset][iFBOffset] ), - 9 ), - fImag_fx ); + fInvSCFGain_fx, + ppiSignImag[iBlockOffset][iFBOffset] ), + 9 ), + fImag_fx ); iBlockOffset++; } /* group length */ } /* groups */ -- GitLab From 55c3c39b143fd763d5ad9a8daaf510fc2c351ab4 Mon Sep 17 00:00:00 2001 From: rtyag Date: Tue, 4 Nov 2025 17:00:57 +1100 Subject: [PATCH 33/36] more bug fixes in lib rend --- apps/renderer.c | 3 +++ lib_rend/lib_rend_fx.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/renderer.c b/apps/renderer.c index f6fd7d171..153661395 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1661,6 +1661,7 @@ int main( outBuffer.pq_fact = &outBuffer.q_factor; Word16 subframe_len = (Word16) ( args.sampleRate / ( 200 ) ); // sample rate /FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES Word16 gd_bits = find_guard_bits( subframe_len ); + Word16 prev_q_fact = Q11; while ( 1 ) { int16_t num_in_channels; @@ -1723,10 +1724,12 @@ int main( *outBuffer.pq_fact = 16 - ( gd_bits ); convertInputBuffer_fx( inpInt16Buffer, numSamplesRead, inBuffer.config.numSamplesPerChannel, num_in_channels, inInt32Buffer, *outBuffer.pq_fact, inBuffer.config.is_cldfb, cldfbAna, &Q_out ); *outBuffer.pq_fact = Q_out; + prev_q_fact = Q_out; } else { memset( inBuffer.data_fx, 0, inBuffer.config.numChannels * inBuffer.config.numSamplesPerChannel * sizeof( Word32 ) ); + *outBuffer.pq_fact = prev_q_fact; } int16_t num_subframes, sf_idx; diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index c2e812b76..7f8ca19b9 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9406,6 +9406,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( move16(); copyBufferTo2dArray_fx( hIvasRend->splitRendEncBuffer, tmpBinaural_buff ); } + *outAudio.pq_fact = *hIvasRend->splitRendEncBuffer.pq_fact; /* Encode split rendering bitstream */ convertBitsBufferToInternalBitsBuff( *hBits, &bits ); @@ -9478,7 +9479,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( cldfb_in_flag, ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, ro_md_flag, - hIvasRend->splitRendEncBuffer.q_factor, + Q_buff, Q_out ) ) != IVAS_ERR_OK ) { return error; -- GitLab From 8a36c53313cb5343a33916d8a6a3abeba61ffedc Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 4 Nov 2025 09:34:47 +0100 Subject: [PATCH 34/36] bring back missing code this fixes post rend output from garbage to actual signal, but scaling still needs to be corrected --- lib_rend/lib_rend_fx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 7f8ca19b9..995bd3ae0 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9485,6 +9485,14 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( return error; } + IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + { + FOR( j = 0; j < BINAURAL_CHANNELS; j++ ) + { + scale_sig32( tmpBinaural_buff[j], L_FRAME48k, sub( *outAudio.pq_fact, Q_out[j] ) ); // *outAudio.pq_fact + } + } + convertInternalBitsBuffToBitsBuffer( hBits, bits ); /* copy over first pose data to outAudio */ -- GitLab From b3e69999cf7534e95d1b6008c02b4d41a0fa6975 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 4 Nov 2025 10:35:37 +0100 Subject: [PATCH 35/36] correctly handle limiter threshold in GetSplitBinauralBitstream --- lib_rend/lib_rend_fx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index 9845416c6..a9402b60f 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -9567,11 +9567,12 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( IF( EQ_16( outAudio.config.is_cldfb, 0 ) ) { + Word32 limiter_threshold = L_lshl( IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); #ifndef DISABLE_LIMITER #ifdef DEBUGGING hIvasRend->numClipping += #endif - limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, IVAS_LIMITER_THRESHOLD, *outAudio.pq_fact ); + limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_threshold, *outAudio.pq_fact ); #endif } -- GitLab From 42bc9c81a6ebe6b0d4d280a36b557f104995b488 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 4 Nov 2025 11:44:34 +0100 Subject: [PATCH 36/36] some BASOP compliance fixes --- lib_rend/lib_rend_fx.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib_rend/lib_rend_fx.c b/lib_rend/lib_rend_fx.c index a9402b60f..9a7a38809 100644 --- a/lib_rend/lib_rend_fx.c +++ b/lib_rend/lib_rend_fx.c @@ -1716,16 +1716,21 @@ static ivas_error alignInputDelay( } #ifdef FIX_1119_SPLIT_RENDERING_VOIP - // NOTE(sgi2knj): The arithmetic below needs to use BASOPs - for ( i = 0; i < inputAudio.config.numChannels; ++i ) + FOR( i = 0; i < inputAudio.config.numChannels; ++i ) { - p_read_channels[i] = inputAudio.data_fx + i * numSamplesToPush; + Word32 tmp; + + tmp = imult3216( L_deposit_l( numSamplesToPush ), i ); + p_read_channels[i] = inputAudio.data_fx + tmp; } ivas_TD_RINGBUF_PushChannels( inputBase->delayBuffer, p_read_channels, numSamplesToPush ); - for ( i = 0; i < inputAudio.config.numChannels; ++i ) + FOR( i = 0; i < inputAudio.config.numChannels; ++i ) { - p_write_channels[i] = inputBase->inputBuffer.data_fx + i * numSamplesToPop; + Word32 tmp; + + tmp = imult3216( L_deposit_l( numSamplesToPop ), i ); + p_write_channels[i] = inputBase->inputBuffer.data_fx + tmp; } ivas_TD_RINGBUF_PopChannels( inputBase->delayBuffer, p_write_channels, numSamplesToPop ); #else @@ -9357,14 +9362,15 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( ISAR_SPLIT_REND_BITS_DATA bits; Word16 max_bands; Word16 Q_out[2]; + Word16 pcm_out_flag; #ifdef FIX_1119_SPLIT_RENDERING_VOIP Word32 *p_Cldfb_RealBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX]; Word32 *p_Cldfb_ImagBuffer_Binaural[MAX_HEAD_ROT_POSES * BINAURAL_CHANNELS][CLDFB_NO_COL_MAX]; Word16 j; - for ( i = 0; i < BINAURAL_CHANNELS * MAX_HEAD_ROT_POSES; ++i ) + FOR( i = 0; i < BINAURAL_CHANNELS * MAX_HEAD_ROT_POSES; ++i ) { - for ( j = 0; j < CLDFB_NO_COL_MAX; ++j ) + FOR( j = 0; j < CLDFB_NO_COL_MAX; ++j ) { p_Cldfb_RealBuffer_Binaural[i][j] = Cldfb_RealBuffer_Binaural[i][j]; p_Cldfb_ImagBuffer_Binaural[i][j] = Cldfb_ImagBuffer_Binaural[i][j]; @@ -9380,6 +9386,15 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( Q_out[1] = Q31; move16(); + pcm_out_flag = 0; + move16(); + + if ( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + { + pcm_out_flag = 1; + move16(); + } + FOR( ch = 0; ch < i_mult( MAX_HEAD_ROT_POSES, BINAURAL_CHANNELS ); ch++ ) { tmpBinaural[ch] = tmpBinaural_buff[ch]; @@ -9520,7 +9535,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( tmpBinaural, 1, cldfb_in_flag, - ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, + pcm_out_flag, ro_md_flag, Q_buff, Q_out ) ) != IVAS_ERR_OK ) @@ -9538,7 +9553,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( tmpBinaural, 1, cldfb_in_flag, - ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ? 1 : 0, + pcm_out_flag, ro_md_flag, Q_buff, Q_out ) ) != IVAS_ERR_OK ) @@ -9547,7 +9562,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( return error; } - IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + IF( pcm_out_flag ) { FOR( j = 0; j < BINAURAL_CHANNELS; j++ ) { @@ -9558,7 +9573,7 @@ ivas_error IVAS_REND_GetSplitBinauralBitstream( convertInternalBitsBuffToBitsBuffer( hBits, bits ); /* copy over first pose data to outAudio */ - IF( EQ_32( hIvasRend->outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + IF( pcm_out_flag ) { /* set outAudio to zero - getSamplesInternal only cleared splitRendEncBuffer */ set_zero_fx( outAudio.data_fx, i_mult( outAudio.config.numChannels, outAudio.config.numSamplesPerChannel ) ); -- GitLab