diff --git a/Workspace_msvc/lib_rend.vcxproj b/Workspace_msvc/lib_rend.vcxproj index c39806d47edcb1f3165b7ddbdcd9643b9e71c6ec..5f78402ecd215c7a91047758c05e9d1dc2c1a271 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 adf664f97d7fe88cbfcf087f98d6106becc71ff2..2d7ba3c8dd3a1135fa999f85ad4c721188a3515f 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 c64e593393ac550472f212fa6107e3ff0e497791..e59a27a46c74147f263fac7fcfafade2de68cb40 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 da4f6542f69b0cfefab4404a2182a7672c1731d3..10adbea2d4aa27a67f68704f5b981037a92bd810 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 512d4fd21d8de391b898979b6481250b0f605f5e..fe2f62ed4e21e787ec503122eb2d0e991a29e91c 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 85c4dedd2eb33676b4ac2164f52822fe262b8f10..48e3dd34903cfd457e8c63d9f8dbc62b3e5fcf6c 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/ivas_td_ring_buffer_fx.c b/lib_rend/ivas_td_ring_buffer_fx.c new file mode 100644 index 0000000000000000000000000000000000000000..0660bc5068e7098610d0b207c05a8e50f0fce423 --- /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; +} diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index 1f46ca932d417270d2b8a2c80c03061291959156..ec447df7a2695692209827995b9c9f78afaf7eb1 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 edf3231d7be3739e313b2a223dc81e1fbaca4fa4..72023e4083ecf0bc5f7960c6ab51e7c4416418ba 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; }