diff --git a/apps/renderer.c b/apps/renderer.c index 77f25ba0b3995317e632e32935dc97063718516b..ee29926e7c9eaa9146f5f4e1d465d91ab1e98ee2 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -132,6 +132,14 @@ typedef struct bool quietModeEnabled; bool sceneDescriptionInput; float inputGainGlobal; /* Linear gain (not in dB) */ +#ifdef REND_CFG_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; typedef enum @@ -146,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, @@ -222,13 +235,26 @@ 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_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" */ .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 }, +#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", @@ -334,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 ); @@ -550,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 ) ) { @@ -656,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 ) @@ -679,7 +729,34 @@ int main( } } +#ifdef REND_CFG_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 ) + { + 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 ) @@ -1206,12 +1283,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 ) ) @@ -1233,18 +1316,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 ); @@ -1259,10 +1356,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( @@ -1357,6 +1462,54 @@ static IVAS_REND_AudioConfig parseAudioConfig( return IVAS_REND_AUDIO_CONFIG_UNKNOWN; } +#ifdef REND_CFG_LFE +static bool parseLfePositionConfig( + 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++; + + /* ignore any additionally specified values */ + if ( nvalues == 3 ) + { + break; + } + } + + if ( nvalues != 3 ) + { + return false; + } + + *lfeGain = values[0]; + *lfeAzimuth = values[1]; + *lfeElevation = values[2]; + return true; +} +#endif + #ifndef FIX_293_EXT_RENDERER_CLI static const CmdLnParser_Option *findOptionById( const int32_t id ) @@ -1456,6 +1609,15 @@ static CmdlnArgs defaultArgs( args.sceneDescriptionInput = false; args.inputGainGlobal = 1.0f; +#ifdef REND_CFG_LFE + args.lfePanningEnabled = false; + args.lfeConfigGain = 1.0f; + args.lfeConfigAzimuth = 0; + args.lfeConfigElevation = 0; + + args.lfeCustomRoutingEnabled = false; + clearString( args.inLfePanningMatrixFile ); +#endif return args; } @@ -1526,7 +1688,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 ); @@ -1534,15 +1700,38 @@ 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; +#ifdef REND_CFG_LFE + case CmdlnOptionId_lfePosition: + assert( numOptionValues == 1 ); + if ( !parseLfePositionConfig( optionValues[0], &args->lfeConfigGain, &args->lfeConfigAzimuth, &args->lfeConfigElevation ) ) + { + fprintf( stderr, "Unknown or invalid option for LFE position: %s\n", optionValues[0] ); + exit( -1 ); + } + 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; @@ -2325,6 +2514,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_com/options.h b/lib_com/options.h index 54c20247d2f63e7cf11ce47b8ab614b263181888..3677da672004b303bfbaf89cda24c60404c7b013 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -148,6 +148,7 @@ #define FIX_I59_DELAY_ROUNDING /* Issue 59: rounding in sample domain instead of nanosec for IVAS_ENC_GetDelay() and IVAS_DEC_GetDelay() */ #define FIX_FIX_I59 /* Issue 59: small fix concerning LFE delay rounding */ #define FIX_245_RANGE_CODER_VOIP_MSAN /* Issue 245: fix use-of-uninitialized-value in range coder in VoIP mode */ +#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) */ #define ENV_STAB_FIX /* Contribution 23: HQ envelope stability memory fix */ #define STABILIZE_GIPD /* FhG: Contribution 22: gIPD stabilization */ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 5e59d8586129bb45d2e0c4d6a41a2f95e448b4f1..e198f3442c2018ec13216917022e38903df4f033 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; + IVAS_REND_LfePanMtx lfePanMtx; +} 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 @@ -606,6 +622,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 ) @@ -1343,6 +1379,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.lfePanMtx[i] ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfePanMtx[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfePanMtx[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.lfePanMtx[i], + outAmbiOrder ); + + /* linear input gain */ + v_multc( inputMc->lfeRouting.lfePanMtx[i], + inputMc->lfeRouting.lfeInputGain, + inputMc->lfeRouting.lfePanMtx[i], + IVAS_MAX_OUTPUT_CHANNELS ); + } + + return error; +} +#endif + static ivas_error updateMcPanGainsForMcOut( input_mc *inputMc, const IVAS_REND_AudioConfig outConfig ) @@ -1384,6 +1511,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; } @@ -1446,6 +1584,14 @@ static ivas_error updateMcPanGainsForAmbiOut( } } +#ifdef REND_CFG_LFE + /* update LFE panning */ + if ( ( error = updateLfePanGainsForAmbiOut( inputMc, outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } +#endif + return IVAS_ERR_OK; } @@ -1495,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; @@ -1578,22 +1724,35 @@ 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. */ 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 + routing.pan_lfe = false; + routing.lfeInputGain = 1.0f; +#endif + switch ( inConfig ) { case IVAS_REND_AUDIO_CONFIG_5_1: @@ -1617,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: @@ -2888,30 +3047,66 @@ ivas_error IVAS_REND_SetInputGain( return IVAS_ERR_OK; } -static int32_t getNumLfeChannels( - input_mc *inputMc ) +#ifdef REND_CFG_LFE +ivas_error IVAS_REND_SetInputLfeMtx( + IVAS_REND_HANDLE hIvasRend, + const IVAS_REND_InputId inputId, + const IVAS_REND_LfePanMtx *lfePanMtx ) { - switch ( inputMc->base.inConfig ) + int16_t i; + input_base *pInputBase; + input_mc *pInputMc; + ivas_error error; + + /*-----------------------------------------------------------------* + * Validate function arguments + *-----------------------------------------------------------------*/ + + if ( hIvasRend == NULL ) { - 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 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; - return 0; + /* 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 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; @@ -2936,12 +3131,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 ea4b84d897ab49108e72e8ad7fd009501bd90619..7ef4664ccf8e759b504ba94c4a232e3e9a05664a 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; @@ -133,11 +137,13 @@ typedef enum typedef uint16_t IVAS_REND_InputId; +#ifndef REND_CFG_LFE typedef struct { int16_t numLfeChannels; float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS]; } IVAS_REND_LfeRouting; +#endif /* clang-format off */ /*----------------------------------------------------------------------------------* @@ -191,10 +197,28 @@ ivas_error IVAS_REND_SetInputGain( const float gain /* i : linear gain (not in dB) */ ); +#ifdef REND_CFG_LFE +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 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(