From fc9b20c887cbb9849284ba113ad775596876a164 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 11 Jan 2023 17:33:20 +0100 Subject: [PATCH 1/7] initial version of configurable LFE handling for renderer --- apps/renderer.c | 113 +++++++++++++++++++++++++++++++ lib_com/options.h | 1 + lib_rend/lib_rend.c | 161 ++++++++++++++++++++++++++++++++++++++------ lib_rend/lib_rend.h | 12 ++++ 4 files changed, 266 insertions(+), 21 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 6084b9f46a..c2efb099c0 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -132,6 +132,11 @@ typedef struct bool quietModeEnabled; bool sceneDescriptionInput; float inputGainGlobal; /* Linear gain (not in dB) */ +#ifdef REND_CFG_LFE + float lfeConfigGain; /* Linear gain (not in dB) */ + float lfeConfigAzimuth; + float lfeConfigElevation; +#endif } CmdlnArgs; typedef enum @@ -222,12 +227,19 @@ static const CmdLnParser_Option cliOptions[] = { .description = "Head orientation tracking type: 'ref' or 'avg' (only for BINAURAL and BINAURAL_ROOM) (todo: check implementation)", }, { +#ifdef REND_CFG_LFE + .id = CmdLnOptionId_customLfeRouting, + .match = "lfe_config", + .matchShort = "lfe", + .description = "LFE Routing configuration. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear (like --gain, -g) and azimuth, elevation are in degrees.\nIf specified, overrides routing of input LFE to output LFE for outputs that have the LFE channel available.", +#else /* TODO(sgi): Replace with more configurable input, e.g. ask for a list of triplets: (gain, azimuth, elevation) to place LFE signal */ /* rename to "lfeHandling" */ .id = CmdLnOptionId_customLfeRouting, .match = "neverDropLfe", .matchShort = "ndl", .description = "[flag] If set, renderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE", +#endif }, { .id = CmdLnOptionId_noDelayCmp, @@ -679,7 +691,15 @@ int main( } } +#ifdef REND_CFG_LFE + if ( ( error = IVAS_REND_SetInputLfeRouting( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } +#else /* TODO(sgi): Test custom LFE routing here */ +#endif } for ( i = 0; i < args.inConfig.numAudioObjects; ++i ) @@ -1175,12 +1195,18 @@ static bool parseOutConfig( return true; } +#ifdef REND_CFG_LFE +static bool parseDiegeticPan( +#else static int8_t parseDiegeticPan( +#endif char *value, float *noDiegeticPan ) { +#ifndef REND_CFG_LFE int8_t success; success = 1; +#endif to_upper( value ); if ( ( strcmp( value, "CENTER" ) == 0 ) || ( strchr( value, 'C' ) != NULL ) ) @@ -1202,18 +1228,32 @@ static int8_t parseDiegeticPan( if ( *noDiegeticPan > 1.0f || *noDiegeticPan < -1.0f ) { fprintf( stderr, "Error: Incorrect value for panning option argument specified!\n\n" ); +#ifdef REND_CFG_LFE + return false; +#else success = 0; +#endif } } +#ifdef REND_CFG_LFE + return false; +#else return success ? 0 : -1; +#endif } +#ifdef REND_CFG_LFE +static bool parseOrientationTracking( +#else static int8_t parseOrientationTracking( +#endif char *value, int8_t *tracking_type ) { +#ifndef REND_CFG_LFE int8_t success; success = 1; +#endif to_upper( value ); @@ -1228,10 +1268,18 @@ static int8_t parseOrientationTracking( else { fprintf( stderr, "Error: Invalid orientation tracking type %s \n\n", value ); +#ifdef REND_CFG_LFE + return false; +#else success = 0; +#endif } +#ifdef REND_CFG_LFE + return true; +#else return success ? 0 : -1; +#endif } static IVAS_REND_AudioConfig parseAudioConfig( @@ -1313,6 +1361,48 @@ static IVAS_REND_AudioConfig parseAudioConfig( return IVAS_REND_AUDIO_CONFIG_UNKNOWN; } +#ifdef REND_CFG_LFE +static bool parseLfeConfig( + const char *value, + float *lfeGain, + float *lfeAzimuth, + float *lfeElevation ) +{ + uint8_t nvalues; + const char *tok; + float values[3]; + char config_string[RENDERER_MAX_METADATA_LINE_LENGTH]; + + strncpy( config_string, value, RENDERER_MAX_METADATA_LINE_LENGTH ); + nvalues = 0; + + for ( tok = strtok( config_string, "," ); tok && *tok; tok = strtok( NULL, ",\n" ) ) + { + while ( *tok == ' ' ) + { + tok++; + } + + if ( *tok == '\0' ) + { + continue; + } + values[nvalues] = (float) atof( tok ); + nvalues++; + } + + if ( nvalues != 3 ) + { + return false; + } + + *lfeGain = values[0]; + *lfeAzimuth = values[1]; + *lfeElevation = values[2]; + return true; +} +#endif + static const CmdLnParser_Option *findOptionById( const int32_t id ) { @@ -1410,6 +1500,11 @@ static CmdlnArgs defaultArgs( args.sceneDescriptionInput = false; args.inputGainGlobal = 1.0f; +#ifdef REND_CFG_LFE + args.lfeConfigGain = 1.0f; + args.lfeConfigAzimuth = 0; + args.lfeConfigElevation = 0; +#endif return args; } @@ -1480,7 +1575,11 @@ static void parseOption( break; case CmdLnOptionId_noDiegeticPan: assert( numOptionValues == 1 ); +#ifdef REND_CFG_LFE + if ( !parseDiegeticPan( optionValues[0], &args->noDiegeticPan ) ) +#else if ( parseDiegeticPan( optionValues[0], &args->noDiegeticPan ) != 0 ) +#endif { fprintf( stderr, "Unknown option for diegetic panning: %s\n", optionValues[0] ); exit( -1 ); @@ -1488,14 +1587,28 @@ static void parseOption( break; case CmdLnOptionId_orientationTracking: assert( numOptionValues == 1 ); +#ifdef REND_CFG_LFE + if ( !parseOrientationTracking( optionValues[0], &args->orientationTracking ) ) +#else if ( parseOrientationTracking( optionValues[0], &args->orientationTracking ) != 0 ) +#endif { fprintf( stderr, "Unknown option for orientation tracking: %s\n", optionValues[0] ); exit( -1 ); } break; case CmdLnOptionId_customLfeRouting: +#ifdef REND_CFG_LFE + assert( numOptionValues == 1 ); + if ( !parseLfeConfig( optionValues[0], &args->lfeConfigGain, &args->lfeConfigAzimuth, &args->lfeConfigElevation ) ) + { + fprintf( stderr, "Unknown or invalid option for LFE configuration: %s\n", optionValues[0] ); + exit( -1 ); + } + break; +#else assert( 0 && "Not yet implemented in CLI" ); +#endif break; case CmdLnOptionId_noDelayCmp: assert( numOptionValues == 0 ); diff --git a/lib_com/options.h b/lib_com/options.h index 332afbbe54..b63ce0d404 100755 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -152,6 +152,7 @@ #define FIX_ANGLE_WRAPPING /* Issue 244: Problems with angle wrapping*/ #define FIX_245_RANGE_CODER_VOIP_MSAN /* Issue 245: fix use-of-uninitialized-value in range coder in VoIP mode */ #define FIX_272_COV /* Issue 272: Cleanup for code coverage related to calls to ivas_binaural_cldfb() */ +#define REND_CFG_LFE /* Issue 110: Configurable LFE handling for external renderer */ #define FIX_235 /* Issue 235: Deallocation of HR filter memory separately for lib_rend (ROM) and lib_util (from file) */ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 028b0f6274..7c94fc3a76 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -606,6 +606,26 @@ static ivas_error getAmbisonicsOrder( return IVAS_ERR_OK; } +static int32_t getNumLfeChannels( + input_mc *inputMc ) +{ + switch ( inputMc->base.inConfig ) + { + case IVAS_REND_AUDIO_CONFIG_5_1: + case IVAS_REND_AUDIO_CONFIG_7_1: + case IVAS_REND_AUDIO_CONFIG_5_1_2: + case IVAS_REND_AUDIO_CONFIG_5_1_4: + case IVAS_REND_AUDIO_CONFIG_7_1_4: + return 1; + case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: + return inputMc->customLsInput.num_lfe; + default: + break; + } + + return 0; +} + static ivas_error getNumNonLfeChannelsInSpeakerLayout( IVAS_REND_AudioConfig config, int16_t *numNonLfeChannels ) @@ -1449,6 +1469,97 @@ static ivas_error updateMcPanGainsForAmbiOut( return IVAS_ERR_OK; } +#ifdef REND_CFG_LFE +static ivas_error updateLfePanGainsForMcOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i, numLfeIn, numOutChannels; + ivas_error error; + error = IVAS_ERR_OK; + + /* If panning is not required, simply return */ + if ( !inputMc->lfeRouting.pan_lfe ) + { + return error; + } + + numLfeIn = getNumLfeChannels( inputMc ); + + if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + numOutChannels = inputMc->base.ctx.pCustomLsOut->num_spk + inputMc->base.ctx.pCustomLsOut->num_lfe; + } + else + { + if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + for ( i = 0; i < numLfeIn; i++ ) + { + /* panning gains */ + if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, + inputMc->lfeRouting.lfeOutputAzimuth, + inputMc->lfeRouting.lfeOutputElevation, + inputMc->lfeRouting.lfeOutputGains[i] ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfeOutputGains[i], + numOutChannels ); + } + + return error; +} + +static ivas_error updateLfePanGainsForAmbiOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i; + int16_t numLfeIn, outAmbiOrder; + ivas_error error; + error = IVAS_ERR_OK; + + /* If panning is not required, simply return */ + if ( !inputMc->lfeRouting.pan_lfe ) + { + return error; + } + + if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK ) + { + return error; + } + + numLfeIn = getNumLfeChannels( inputMc ); + + for ( i = 0; i < numLfeIn; i++ ) + { + /* panning gains */ + ivas_dirac_dec_get_response( inputMc->lfeRouting.lfeOutputAzimuth, + inputMc->lfeRouting.lfeOutputElevation, + inputMc->lfeRouting.lfeOutputGains[i], + outAmbiOrder ); + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfeOutputGains[i], + IVAS_MAX_OUTPUT_CHANNELS ); + } + + return error; +} +#endif + static ivas_error updateMcPanGains( input_mc *inputMc, const IVAS_REND_AudioConfig outConfig ) @@ -1464,9 +1575,15 @@ static ivas_error updateMcPanGains( { case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: error = updateMcPanGainsForMcOut( inputMc, outConfig ); +#ifdef REND_CFG_LFE + error = updateLfePanGainsForMcOut( inputMc, outConfig ); +#endif break; case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: error = updateMcPanGainsForAmbiOut( inputMc, outConfig ); +#ifdef REND_CFG_LFE + error = updateLfePanGainsForAmbiOut( inputMc, outConfig ); +#endif break; case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: switch ( outConfig ) @@ -1476,6 +1593,9 @@ static ivas_error updateMcPanGains( case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: /* Prepare rendering to intermediate format */ error = updateMcPanGainsForMcOut( inputMc, IVAS_REND_AUDIO_CONFIG_7_1_4 ); +#ifdef REND_CFG_LFE + error = updateLfePanGainsForMcOut( inputMc, outConfig ); +#endif break; default: return IVAS_ERR_INVALID_OUTPUT_FORMAT; @@ -1594,6 +1714,11 @@ static IVAS_REND_LfeRouting defaultLfeRouting( set_zero( routing.lfeOutputGains[i], IVAS_MAX_OUTPUT_CHANNELS ); } +#ifdef REND_CFG_LFE + routing.pan_lfe = false; + routing.lfeInputGain = 1.0f; +#endif + switch ( inConfig ) { case IVAS_REND_AUDIO_CONFIG_5_1: @@ -2884,30 +3009,17 @@ ivas_error IVAS_REND_SetInputGain( return IVAS_ERR_OK; } -static int32_t getNumLfeChannels( - input_mc *inputMc ) -{ - switch ( inputMc->base.inConfig ) - { - case IVAS_REND_AUDIO_CONFIG_5_1: - case IVAS_REND_AUDIO_CONFIG_7_1: - case IVAS_REND_AUDIO_CONFIG_5_1_2: - case IVAS_REND_AUDIO_CONFIG_5_1_4: - case IVAS_REND_AUDIO_CONFIG_7_1_4: - return 1; - case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: - return inputMc->customLsInput.num_lfe; - default: - break; - } - - return 0; -} - ivas_error IVAS_REND_SetInputLfeRouting( IVAS_REND_HANDLE hIvasRend, const IVAS_REND_InputId inputId, - const IVAS_REND_LfeRouting lfeRouting ) +#ifdef REND_CFG_LFE + const float inputGain, + const float outputAzimuth, + const float outputElevation +#else + const IVAS_REND_LfeRouting lfeRouting +#endif +) { input_base *pInputBase; input_mc *pInputMc; @@ -2932,12 +3044,19 @@ ivas_error IVAS_REND_SetInputLfeRouting( } pInputMc = (input_mc *) pInputBase; +#ifdef REND_CFG_LFE + pInputMc->lfeRouting.pan_lfe = true; + pInputMc->lfeRouting.lfeInputGain = inputGain; + pInputMc->lfeRouting.lfeOutputAzimuth = outputAzimuth; + pInputMc->lfeRouting.lfeOutputElevation = outputElevation; +#else if ( getNumLfeChannels( pInputMc ) != lfeRouting.numLfeChannels ) { return IVAS_ERR_WRONG_NUM_CHANNELS; } pInputMc->lfeRouting = lfeRouting; +#endif if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) { return error; diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index ea4b84d897..cd523b2e34 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -136,6 +136,12 @@ typedef uint16_t IVAS_REND_InputId; typedef struct { int16_t numLfeChannels; +#ifdef REND_CFG_LFE + bool pan_lfe; + float lfeInputGain; + float lfeOutputAzimuth; + float lfeOutputElevation; +#endif float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; } IVAS_REND_LfeRouting; @@ -194,7 +200,13 @@ ivas_error IVAS_REND_SetInputGain( ivas_error IVAS_REND_SetInputLfeRouting( IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ const IVAS_REND_InputId inputId, /* i : ID of the input */ +#ifdef REND_CFG_LFE + const float inputGain, /* i : Input gain to be applied to the LFE channel(s) */ + const float outputAzimuth, /* i : Output azimuth position */ + const float outputElevation /* i : Output elevation position */ +#else const IVAS_REND_LfeRouting lfeRouting /* i : custom LFE routing struct */ +#endif ); ivas_error IVAS_REND_RemoveInput( -- GitLab From 78177a5cca4880227b393fa3fb88486edd5cdb26 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 11 Jan 2023 17:56:24 +0100 Subject: [PATCH 2/7] set a flag to control when LFE panning is performed, should resolve non-BE --- apps/renderer.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index c2efb099c0..008ad189ad 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -133,6 +133,7 @@ typedef struct bool sceneDescriptionInput; float inputGainGlobal; /* Linear gain (not in dB) */ #ifdef REND_CFG_LFE + bool pan_lfe; float lfeConfigGain; /* Linear gain (not in dB) */ float lfeConfigAzimuth; float lfeConfigElevation; @@ -692,10 +693,13 @@ int main( } #ifdef REND_CFG_LFE - if ( ( error = IVAS_REND_SetInputLfeRouting( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) + if ( args.pan_lfe ) { - fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); - exit( -1 ); + if ( ( error = IVAS_REND_SetInputLfeRouting( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } } #else /* TODO(sgi): Test custom LFE routing here */ @@ -1605,6 +1609,7 @@ static void parseOption( fprintf( stderr, "Unknown or invalid option for LFE configuration: %s\n", optionValues[0] ); exit( -1 ); } + args->pan_lfe = true; break; #else assert( 0 && "Not yet implemented in CLI" ); -- GitLab From 384239a493e47b5628993da2c7f59541c4e3657f Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 11 Jan 2023 18:04:09 +0100 Subject: [PATCH 3/7] fix MSAN error, default wasn't set --- apps/renderer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/renderer.c b/apps/renderer.c index 008ad189ad..9f2898e4f8 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1505,6 +1505,7 @@ static CmdlnArgs defaultArgs( args.inputGainGlobal = 1.0f; #ifdef REND_CFG_LFE + args.pan_lfe = false; args.lfeConfigGain = 1.0f; args.lfeConfigAzimuth = 0; args.lfeConfigElevation = 0; -- GitLab From 3cce8239d7d8122d747338c30fc683afa46004be Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 12 Jan 2023 12:32:44 +0100 Subject: [PATCH 4/7] fix asan error and refactor function calls into update McPanGains... --- apps/renderer.c | 6 ++ lib_rend/lib_rend.c | 202 +++++++++++++++++++++++--------------------- 2 files changed, 112 insertions(+), 96 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 9f2898e4f8..70ddbf3cf0 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1393,6 +1393,12 @@ static bool parseLfeConfig( } values[nvalues] = (float) atof( tok ); nvalues++; + + /* ignore any additionally specified values */ + if ( nvalues == 3 ) + { + break; + } } if ( nvalues != 3 ) diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 7c94fc3a76..576ebffd42 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -1363,6 +1363,97 @@ static bool configsAreEqual( return configA == configB; } +#ifdef REND_CFG_LFE +static ivas_error updateLfePanGainsForMcOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i, numLfeIn, numOutChannels; + ivas_error error; + error = IVAS_ERR_OK; + + /* If panning is not required, simply return */ + if ( !inputMc->lfeRouting.pan_lfe ) + { + return error; + } + + numLfeIn = getNumLfeChannels( inputMc ); + + if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) + { + numOutChannels = inputMc->base.ctx.pCustomLsOut->num_spk + inputMc->base.ctx.pCustomLsOut->num_lfe; + } + else + { + if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + for ( i = 0; i < numLfeIn; i++ ) + { + /* panning gains */ + if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, + inputMc->lfeRouting.lfeOutputAzimuth, + inputMc->lfeRouting.lfeOutputElevation, + inputMc->lfeRouting.lfeOutputGains[i] ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfeOutputGains[i], + numOutChannels ); + } + + return error; +} + +static ivas_error updateLfePanGainsForAmbiOut( + input_mc *inputMc, + const IVAS_REND_AudioConfig outConfig ) +{ + int16_t i; + int16_t numLfeIn, outAmbiOrder; + ivas_error error; + error = IVAS_ERR_OK; + + /* If panning is not required, simply return */ + if ( !inputMc->lfeRouting.pan_lfe ) + { + return error; + } + + if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK ) + { + return error; + } + + numLfeIn = getNumLfeChannels( inputMc ); + + for ( i = 0; i < numLfeIn; i++ ) + { + /* panning gains */ + ivas_dirac_dec_get_response( inputMc->lfeRouting.lfeOutputAzimuth, + inputMc->lfeRouting.lfeOutputElevation, + inputMc->lfeRouting.lfeOutputGains[i], + outAmbiOrder ); + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfeOutputGains[i], + IVAS_MAX_OUTPUT_CHANNELS ); + } + + return error; +} +#endif + static ivas_error updateMcPanGainsForMcOut( input_mc *inputMc, const IVAS_REND_AudioConfig outConfig ) @@ -1404,6 +1495,17 @@ static ivas_error updateMcPanGainsForMcOut( error = initMcPanGainsWithConversionMapping( inputMc, outConfig ); } +#ifdef REND_CFG_LFE + /* check for errors from above block */ + if ( error != IVAS_ERR_OK ) + { + return error; + } + + /* update LFE panning */ + error = updateLfePanGainsForMcOut( inputMc, outConfig ); +#endif + return error; } @@ -1466,99 +1568,16 @@ static ivas_error updateMcPanGainsForAmbiOut( } } - return IVAS_ERR_OK; -} - #ifdef REND_CFG_LFE -static ivas_error updateLfePanGainsForMcOut( - input_mc *inputMc, - const IVAS_REND_AudioConfig outConfig ) -{ - int16_t i, numLfeIn, numOutChannels; - ivas_error error; - error = IVAS_ERR_OK; - - /* If panning is not required, simply return */ - if ( !inputMc->lfeRouting.pan_lfe ) + /* update LFE panning */ + if ( ( error = updateLfePanGainsForAmbiOut( inputMc, outConfig ) ) != IVAS_ERR_OK ) { return error; } +#endif - numLfeIn = getNumLfeChannels( inputMc ); - - if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) - { - numOutChannels = inputMc->base.ctx.pCustomLsOut->num_spk + inputMc->base.ctx.pCustomLsOut->num_lfe; - } - else - { - if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK ) - { - return error; - } - } - - for ( i = 0; i < numLfeIn; i++ ) - { - /* panning gains */ - if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, - inputMc->lfeRouting.lfeOutputAzimuth, - inputMc->lfeRouting.lfeOutputElevation, - inputMc->lfeRouting.lfeOutputGains[i] ) ) != IVAS_ERR_OK ) - { - return error; - } - - /* linear input gain */ - v_multc( inputMc->lfeRouting.lfeOutputGains[i], - inputMc->lfeRouting.lfeInputGain, - inputMc->lfeRouting.lfeOutputGains[i], - numOutChannels ); - } - - return error; -} - -static ivas_error updateLfePanGainsForAmbiOut( - input_mc *inputMc, - const IVAS_REND_AudioConfig outConfig ) -{ - int16_t i; - int16_t numLfeIn, outAmbiOrder; - ivas_error error; - error = IVAS_ERR_OK; - - /* If panning is not required, simply return */ - if ( !inputMc->lfeRouting.pan_lfe ) - { - return error; - } - - if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK ) - { - return error; - } - - numLfeIn = getNumLfeChannels( inputMc ); - - for ( i = 0; i < numLfeIn; i++ ) - { - /* panning gains */ - ivas_dirac_dec_get_response( inputMc->lfeRouting.lfeOutputAzimuth, - inputMc->lfeRouting.lfeOutputElevation, - inputMc->lfeRouting.lfeOutputGains[i], - outAmbiOrder ); - - /* linear input gain */ - v_multc( inputMc->lfeRouting.lfeOutputGains[i], - inputMc->lfeRouting.lfeInputGain, - inputMc->lfeRouting.lfeOutputGains[i], - IVAS_MAX_OUTPUT_CHANNELS ); - } - - return error; + return IVAS_ERR_OK; } -#endif static ivas_error updateMcPanGains( input_mc *inputMc, @@ -1575,15 +1594,9 @@ static ivas_error updateMcPanGains( { case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: error = updateMcPanGainsForMcOut( inputMc, outConfig ); -#ifdef REND_CFG_LFE - error = updateLfePanGainsForMcOut( inputMc, outConfig ); -#endif break; case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: error = updateMcPanGainsForAmbiOut( inputMc, outConfig ); -#ifdef REND_CFG_LFE - error = updateLfePanGainsForAmbiOut( inputMc, outConfig ); -#endif break; case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: switch ( outConfig ) @@ -1593,9 +1606,6 @@ static ivas_error updateMcPanGains( case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM: /* Prepare rendering to intermediate format */ error = updateMcPanGainsForMcOut( inputMc, IVAS_REND_AUDIO_CONFIG_7_1_4 ); -#ifdef REND_CFG_LFE - error = updateLfePanGainsForMcOut( inputMc, outConfig ); -#endif break; default: return IVAS_ERR_INVALID_OUTPUT_FORMAT; -- GitLab From 10293bc82e2fcf429b22bb32d87e06035ffa55d3 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 12 Jan 2023 12:45:59 +0100 Subject: [PATCH 5/7] rename API function and make LFE routing struct private --- apps/renderer.c | 2 +- lib_rend/lib_rend.c | 28 ++++++++++++++++++++++++++++ lib_rend/lib_rend.h | 12 ++++++------ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 70ddbf3cf0..b1134ee4a2 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -695,7 +695,7 @@ int main( #ifdef REND_CFG_LFE if ( args.pan_lfe ) { - if ( ( error = IVAS_REND_SetInputLfeRouting( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_SetInputLfePan( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) { fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); exit( -1 ); diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 576ebffd42..2ebabf8645 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -115,6 +115,18 @@ typedef struct rotation_matrix rot_mat_prev; } input_ism; +#ifdef REND_CFG_LFE +typedef struct +{ + int16_t numLfeChannels; + bool pan_lfe; + float lfeInputGain; + float lfeOutputAzimuth; + float lfeOutputElevation; + float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; +} lfe_routing; +#endif + typedef struct { input_base base; @@ -128,7 +140,11 @@ typedef struct TDREND_WRAPPER tdRendWrapper; CREND_WRAPPER crendWrapper; rotation_gains rot_gains_prev; +#ifdef REND_CFG_LFE + lfe_routing lfeRouting; +#else IVAS_REND_LfeRouting lfeRouting; +#endif } input_mc; typedef struct @@ -1708,14 +1724,22 @@ static ivas_error initMcBinauralRendering( return error; } +#ifdef REND_CFG_LFE +static lfe_routing defaultLfeRouting( +#else static IVAS_REND_LfeRouting defaultLfeRouting( +#endif IVAS_REND_AudioConfig inConfig, LSSETUP_CUSTOM_STRUCT customLsIn, IVAS_REND_AudioConfig outConfig, LSSETUP_CUSTOM_STRUCT customLsOut ) { int32_t i; +#ifdef REND_CFG_LFE + lfe_routing routing; +#else IVAS_REND_LfeRouting routing; +#endif /* Set all output gains to zero, then route each input LFE consecutively to the next available output LFE. */ @@ -3019,7 +3043,11 @@ ivas_error IVAS_REND_SetInputGain( return IVAS_ERR_OK; } +#ifdef REND_CFG_LFE +ivas_error IVAS_REND_SetInputLfePan( +#else ivas_error IVAS_REND_SetInputLfeRouting( +#endif IVAS_REND_HANDLE hIvasRend, const IVAS_REND_InputId inputId, #ifdef REND_CFG_LFE diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index cd523b2e34..a704be14ba 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -133,17 +133,13 @@ typedef enum typedef uint16_t IVAS_REND_InputId; +#ifndef REND_CFG_LFE typedef struct { int16_t numLfeChannels; -#ifdef REND_CFG_LFE - bool pan_lfe; - float lfeInputGain; - float lfeOutputAzimuth; - float lfeOutputElevation; -#endif float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; } IVAS_REND_LfeRouting; +#endif /* clang-format off */ /*----------------------------------------------------------------------------------* @@ -197,7 +193,11 @@ ivas_error IVAS_REND_SetInputGain( const float gain /* i : linear gain (not in dB) */ ); +#ifdef REND_CFG_LFE +ivas_error IVAS_REND_SetInputLfePan( +#else ivas_error IVAS_REND_SetInputLfeRouting( +#endif IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ const IVAS_REND_InputId inputId, /* i : ID of the input */ #ifdef REND_CFG_LFE -- GitLab From 59f3c4ab1f923adcf8d1addc6b1968e8e6a8ed83 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 12 Jan 2023 14:07:31 +0100 Subject: [PATCH 6/7] add IVAS_REND_SetInputLfeMtx() API function --- apps/renderer.c | 2 +- lib_rend/lib_rend.c | 71 ++++++++++++++++++++++++++++++++++++--------- lib_rend/lib_rend.h | 14 ++++++++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index b1134ee4a2..6b7549c376 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -695,7 +695,7 @@ int main( #ifdef REND_CFG_LFE if ( args.pan_lfe ) { - if ( ( error = IVAS_REND_SetInputLfePan( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_REND_SetInputLfePos( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) { fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); exit( -1 ); diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 2ebabf8645..206985c23d 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -123,7 +123,7 @@ typedef struct float lfeInputGain; float lfeOutputAzimuth; float lfeOutputElevation; - float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; + IVAS_REND_LfePanMtx lfePanMtx; } lfe_routing; #endif @@ -1414,15 +1414,15 @@ static ivas_error updateLfePanGainsForMcOut( if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, inputMc->lfeRouting.lfeOutputAzimuth, inputMc->lfeRouting.lfeOutputElevation, - inputMc->lfeRouting.lfeOutputGains[i] ) ) != IVAS_ERR_OK ) + inputMc->lfeRouting.lfePanMtx[i] ) ) != IVAS_ERR_OK ) { return error; } /* linear input gain */ - v_multc( inputMc->lfeRouting.lfeOutputGains[i], + v_multc( inputMc->lfeRouting.lfePanMtx[i], inputMc->lfeRouting.lfeInputGain, - inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfePanMtx[i], numOutChannels ); } @@ -1456,13 +1456,13 @@ static ivas_error updateLfePanGainsForAmbiOut( /* panning gains */ ivas_dirac_dec_get_response( inputMc->lfeRouting.lfeOutputAzimuth, inputMc->lfeRouting.lfeOutputElevation, - inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfePanMtx[i], outAmbiOrder ); /* linear input gain */ - v_multc( inputMc->lfeRouting.lfeOutputGains[i], + v_multc( inputMc->lfeRouting.lfePanMtx[i], inputMc->lfeRouting.lfeInputGain, - inputMc->lfeRouting.lfeOutputGains[i], + inputMc->lfeRouting.lfePanMtx[i], IVAS_MAX_OUTPUT_CHANNELS ); } @@ -1641,13 +1641,13 @@ static ivas_error updateMcPanGains( { for ( i = 0; i < inputMc->customLsInput.num_lfe; ++i ) { - mvr2r( inputMc->lfeRouting.lfeOutputGains[i], inputMc->panGains[inputMc->customLsInput.lfe_idx[i]], IVAS_MAX_OUTPUT_CHANNELS ); + mvr2r( inputMc->lfeRouting.lfePanMtx[i], inputMc->panGains[inputMc->customLsInput.lfe_idx[i]], IVAS_MAX_OUTPUT_CHANNELS ); } } else { /* For code simplicity, always copy LFE gains. If config has no LFE, gains will be zero anyway. */ - mvr2r( inputMc->lfeRouting.lfeOutputGains[0], inputMc->panGains[LFE_CHANNEL], IVAS_MAX_OUTPUT_CHANNELS ); + mvr2r( inputMc->lfeRouting.lfePanMtx[0], inputMc->panGains[LFE_CHANNEL], IVAS_MAX_OUTPUT_CHANNELS ); } return IVAS_ERR_OK; @@ -1745,7 +1745,7 @@ static IVAS_REND_LfeRouting defaultLfeRouting( for ( i = 0; i < IVAS_MAX_INPUT_LFE_CHANNELS; ++i ) { - set_zero( routing.lfeOutputGains[i], IVAS_MAX_OUTPUT_CHANNELS ); + set_zero( routing.lfePanMtx[i], IVAS_MAX_OUTPUT_CHANNELS ); } #ifdef REND_CFG_LFE @@ -1776,12 +1776,12 @@ static IVAS_REND_LfeRouting defaultLfeRouting( case IVAS_REND_AUDIO_CONFIG_5_1_4: case IVAS_REND_AUDIO_CONFIG_7_1: case IVAS_REND_AUDIO_CONFIG_7_1_4: - routing.lfeOutputGains[0][LFE_CHANNEL] = 1.0f; + routing.lfePanMtx[0][LFE_CHANNEL] = 1.0f; break; case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM: for ( i = 0; i < routing.numLfeChannels && i < customLsOut.num_lfe; ++i ) { - routing.lfeOutputGains[i][customLsOut.lfe_idx[i]] = 1.0f; + routing.lfePanMtx[i][customLsOut.lfe_idx[i]] = 1.0f; } break; default: @@ -3044,7 +3044,52 @@ ivas_error IVAS_REND_SetInputGain( } #ifdef REND_CFG_LFE -ivas_error IVAS_REND_SetInputLfePan( +ivas_error IVAS_REND_SetInputLfeMtx( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_REND_LfePanMtx lfePanMtx ) +{ + int16_t i; + input_base *pInputBase; + input_mc *pInputMc; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + if ( ( error = getInputById( hIvasRend, inputId, (void **) &pInputBase ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( getAudioConfigType( pInputBase->inConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED ) + { + /* Custom LFE panning matrix only makes sense with channel-based input */ + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + pInputMc = (input_mc *) pInputBase; + + /* copy LFE panning matrix */ + for ( i = 0; i < IVAS_MAX_INPUT_LFE_CHANNELS; i++ ) + { + mvr2r( lfePanMtx[i], pInputMc->lfeRouting.lfePanMtx[i], IVAS_MAX_OUTPUT_CHANNELS ); + } + + if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} +#endif + +#ifdef REND_CFG_LFE +ivas_error IVAS_REND_SetInputLfePos( #else ivas_error IVAS_REND_SetInputLfeRouting( #endif diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index a704be14ba..4bcce88400 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -49,6 +49,10 @@ #define RENDERER_HEAD_POSITIONS_PER_FRAME 4 +#ifdef REND_CFG_LFE +typedef float IVAS_REND_LfePanMtx[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; +#endif + typedef struct { int16_t numSamplesPerChannel; @@ -194,7 +198,15 @@ ivas_error IVAS_REND_SetInputGain( ); #ifdef REND_CFG_LFE -ivas_error IVAS_REND_SetInputLfePan( +ivas_error IVAS_REND_SetInputLfeMtx( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + const IVAS_REND_InputId inputId, /* i : ID of the input */ + const IVAS_REND_LfePanMtx lfePanMtx /* i : LFE panning matrix */ +); +#endif + +#ifdef REND_CFG_LFE +ivas_error IVAS_REND_SetInputLfePos( #else ivas_error IVAS_REND_SetInputLfeRouting( #endif -- GitLab From 6206d93fe94c0836ecd51a10463ed504811fb49e Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Mon, 16 Jan 2023 16:21:29 +0100 Subject: [PATCH 7/7] add support for a panning matrix in CLI via a text file --- apps/renderer.c | 154 ++++++++++++++++++++++++++++++++++++++++---- lib_rend/lib_rend.c | 4 +- lib_rend/lib_rend.h | 2 +- 3 files changed, 145 insertions(+), 15 deletions(-) diff --git a/apps/renderer.c b/apps/renderer.c index 6b7549c376..841920e96c 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -133,10 +133,12 @@ typedef struct bool sceneDescriptionInput; float inputGainGlobal; /* Linear gain (not in dB) */ #ifdef REND_CFG_LFE - bool pan_lfe; + bool lfePanningEnabled; float lfeConfigGain; /* Linear gain (not in dB) */ float lfeConfigAzimuth; float lfeConfigElevation; + bool lfeCustomRoutingEnabled; + char inLfePanningMatrixFile[RENDERER_MAX_CLI_ARG_LENGTH]; #endif } CmdlnArgs; @@ -152,7 +154,12 @@ typedef enum CmdLnOptionId_renderConfigFile, CmdLnOptionId_noDiegeticPan, CmdLnOptionId_orientationTracking, +#ifdef REND_CFG_LFE + CmdlnOptionId_lfePosition, + CmdlnOptionId_lfeMatrix, +#else CmdLnOptionId_customLfeRouting, +#endif CmdLnOptionId_noDelayCmp, CmdLnOptionId_quietModeEnabled, CmdLnOptionId_inputMetadata, @@ -229,10 +236,10 @@ static const CmdLnParser_Option cliOptions[] = { }, { #ifdef REND_CFG_LFE - .id = CmdLnOptionId_customLfeRouting, - .match = "lfe_config", - .matchShort = "lfe", - .description = "LFE Routing configuration. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear (like --gain, -g) and azimuth, elevation are in degrees.\nIf specified, overrides routing of input LFE to output LFE for outputs that have the LFE channel available.", + .id = CmdlnOptionId_lfePosition, + .match = "lfe_position", + .matchShort = "lp", + .description = "Output LFE position. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear (like --gain, -g) and azimuth, elevation are in degrees.\nIf specified, overrides the default behavior which attempts to map input to output LFE channel(s)", #else /* TODO(sgi): Replace with more configurable input, e.g. ask for a list of triplets: (gain, azimuth, elevation) to place LFE signal */ /* rename to "lfeHandling" */ @@ -242,6 +249,12 @@ static const CmdLnParser_Option cliOptions[] = { .description = "[flag] If set, renderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE", #endif }, +#ifdef REND_CFG_LFE + { .id = CmdlnOptionId_lfeMatrix, + .match = "lfe_matrix", + .matchShort = "lm", + .description = "LFE panning matrix. File (CSV table) containing a matrix of dimensions [ num_input_lfe x num_output_channels ] with elements specifying linear routing gain (like --gain, -g). \nIf specified, overrides the output LFE position option and the default behavior which attempts to map input to output LFE channel(s)" }, +#endif { .id = CmdLnOptionId_noDelayCmp, .match = "no_delay_cmp", @@ -347,6 +360,12 @@ static void parseMetadata( IsmPositionProvider *positionProvider, MasaFileReader **masaReaders ); +#ifdef REND_CFG_LFE +static ivas_error parseLfePanMtxFile( + const char *lfeRoutingMatrixFilePath, + IVAS_REND_LfePanMtx *lfePanMtx ); +#endif + static void convert_backslash( char *str ); static void remove_cr( char *str ); @@ -563,6 +582,9 @@ int main( convert_backslash( args.inputFilePath ); convert_backslash( args.outputFilePath ); convert_backslash( args.headRotationFilePath ); +#ifdef REND_CFG_LFE + convert_backslash( args.inLfePanningMatrixFile ); +#endif if ( !isEmptyString( args.headRotationFilePath ) ) { @@ -669,6 +691,21 @@ int main( } } +#ifdef REND_CFG_LFE + IVAS_REND_LfePanMtx lfePanMatrix; + + /* parse input LFE panning matrix */ + if ( args.lfeCustomRoutingEnabled && !isEmptyString( args.inLfePanningMatrixFile ) ) + { + /* TODO tmu: how should we handle this on CLI for multiple MC inputs? */ + if ( ( error = parseLfePanMtxFile( args.inLfePanningMatrixFile, &lfePanMatrix ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } +#endif + for ( i = 0; i < args.inConfig.numMultiChannelBuses; ++i ) { if ( ( error = IVAS_REND_AddInput( hIvasRend, args.inConfig.multiChannelBuses[i].audioConfig, &mcIds[i] ) ) != IVAS_ERR_OK ) @@ -693,7 +730,23 @@ int main( } #ifdef REND_CFG_LFE - if ( args.pan_lfe ) + /* set panning matrix for input LFE */ + if ( args.lfeCustomRoutingEnabled ) + { + if ( args.lfePanningEnabled ) + { + fprintf( stdout, "Warning LFE position specified as well as panning matrix! Ignoring position and using gains from panning matrix\n" ); + args.lfePanningEnabled = false; + } + + if ( ( error = IVAS_REND_SetInputLfeMtx( hIvasRend, mcIds[i], (const IVAS_REND_LfePanMtx *) &lfePanMatrix ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + /* set panning gains for input LFE */ + else if ( args.lfePanningEnabled ) { if ( ( error = IVAS_REND_SetInputLfePos( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK ) { @@ -1366,7 +1419,7 @@ static IVAS_REND_AudioConfig parseAudioConfig( } #ifdef REND_CFG_LFE -static bool parseLfeConfig( +static bool parseLfePositionConfig( const char *value, float *lfeGain, float *lfeAzimuth, @@ -1511,10 +1564,13 @@ static CmdlnArgs defaultArgs( args.inputGainGlobal = 1.0f; #ifdef REND_CFG_LFE - args.pan_lfe = false; + args.lfePanningEnabled = false; args.lfeConfigGain = 1.0f; args.lfeConfigAzimuth = 0; args.lfeConfigElevation = 0; + + args.lfeCustomRoutingEnabled = false; + clearString( args.inLfePanningMatrixFile ); #endif return args; } @@ -1608,20 +1664,28 @@ static void parseOption( exit( -1 ); } break; - case CmdLnOptionId_customLfeRouting: #ifdef REND_CFG_LFE + case CmdlnOptionId_lfePosition: assert( numOptionValues == 1 ); - if ( !parseLfeConfig( optionValues[0], &args->lfeConfigGain, &args->lfeConfigAzimuth, &args->lfeConfigElevation ) ) + if ( !parseLfePositionConfig( optionValues[0], &args->lfeConfigGain, &args->lfeConfigAzimuth, &args->lfeConfigElevation ) ) { - fprintf( stderr, "Unknown or invalid option for LFE configuration: %s\n", optionValues[0] ); + fprintf( stderr, "Unknown or invalid option for LFE position: %s\n", optionValues[0] ); exit( -1 ); } - args->pan_lfe = true; + args->lfePanningEnabled = true; break; #else + case CmdLnOptionId_customLfeRouting: assert( 0 && "Not yet implemented in CLI" ); #endif break; +#ifdef REND_CFG_LFE + case CmdlnOptionId_lfeMatrix: + assert( numOptionValues == 1 ); + strncpy( args->inLfePanningMatrixFile, optionValues[0], RENDERER_MAX_CLI_ARG_LENGTH - 1 ); + args->lfeCustomRoutingEnabled = true; + break; +#endif case CmdLnOptionId_noDelayCmp: assert( numOptionValues == 0 ); args->delayCompensationEnabled = false; @@ -2381,6 +2445,72 @@ static void printSupportedAudioConfigs() return; } +#ifdef REND_CFG_LFE +static ivas_error parseLfePanMtxFile( + const char *lfeRoutingMatrixFilePath, + IVAS_REND_LfePanMtx *lfePanMtx ) +{ + int16_t lfe_in, ch_out; + const char *tok; + char line[200]; /* > (10 chars * IVAS_MAX_OUTPUT_CHANNELS) i.e. "-999, " */ + FILE *mtxFile; + + if ( strlen( lfeRoutingMatrixFilePath ) < 1 ) + { + return IVAS_ERR_FAILED_FILE_OPEN; + } + + mtxFile = fopen( lfeRoutingMatrixFilePath, "r" ); + + if ( !mtxFile ) + { + return IVAS_ERR_FAILED_FILE_OPEN; + } + + /* set default panning matrix to all zeros + any subsequent issue in file reading will gracefully exit the function */ + for ( lfe_in = 0; lfe_in < IVAS_MAX_INPUT_LFE_CHANNELS; lfe_in++ ) + { + set_zero( ( *lfePanMtx )[lfe_in], IVAS_MAX_OUTPUT_CHANNELS ); + } + + for ( lfe_in = 0, ch_out = 0; lfe_in < IVAS_MAX_INPUT_LFE_CHANNELS; lfe_in++ ) + { + /* if EOF or a blank line is encountered, simply return */ + if ( ( fgets( line, 200, mtxFile ) == NULL ) && ( strcmp( line, "\n" ) == 0 ) && ( strcmp( line, "\r\n" ) == 0 ) ) + { + fclose( mtxFile ); + return IVAS_ERR_OK; + } + + for ( tok = strtok( line, "," ); tok && *tok; tok = strtok( NULL, ",\n" ) ) + { + while ( *tok == ' ' ) + { + tok++; + } + + if ( *tok == '\0' ) + { + continue; + } + if ( ch_out > IVAS_MAX_OUTPUT_CHANNELS ) + { + break; + } + else + { + ( *lfePanMtx )[lfe_in][ch_out] = (float) atof( tok ); + ch_out++; + } + } + } + + fclose( mtxFile ); + return IVAS_ERR_OK; +} +#endif + // VE2AT: possibly move these functions to cmdln_parser.c ? static void convert_backslash( char *str ) diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 206985c23d..b92165ae28 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -3047,7 +3047,7 @@ ivas_error IVAS_REND_SetInputGain( ivas_error IVAS_REND_SetInputLfeMtx( IVAS_REND_HANDLE hIvasRend, const IVAS_REND_InputId inputId, - const IVAS_REND_LfePanMtx lfePanMtx ) + const IVAS_REND_LfePanMtx *lfePanMtx ) { int16_t i; input_base *pInputBase; @@ -3076,7 +3076,7 @@ ivas_error IVAS_REND_SetInputLfeMtx( /* copy LFE panning matrix */ for ( i = 0; i < IVAS_MAX_INPUT_LFE_CHANNELS; i++ ) { - mvr2r( lfePanMtx[i], pInputMc->lfeRouting.lfePanMtx[i], IVAS_MAX_OUTPUT_CHANNELS ); + mvr2r( ( *lfePanMtx )[i], pInputMc->lfeRouting.lfePanMtx[i], IVAS_MAX_OUTPUT_CHANNELS ); } if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK ) diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index 4bcce88400..7ef4664ccf 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -201,7 +201,7 @@ ivas_error IVAS_REND_SetInputGain( ivas_error IVAS_REND_SetInputLfeMtx( IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ const IVAS_REND_InputId inputId, /* i : ID of the input */ - const IVAS_REND_LfePanMtx lfePanMtx /* i : LFE panning matrix */ + const IVAS_REND_LfePanMtx *lfePanMtx /* i : LFE panning matrix */ ); #endif -- GitLab