Commit bc144c0a authored by Archit Tamarapu's avatar Archit Tamarapu
Browse files

Merge branch 'main' of forge.3gpp.org:ivas-codec-pc/ivas-codec into FhG/rend_cfg_lfe

parents fce62c2e 0f6c6a6a
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -65,3 +65,6 @@ __pycache__/

#history
.history/

# coan output files that are created when cleaning out switches
coan_out_*
+19 −62
Original line number Diff line number Diff line
@@ -201,26 +201,6 @@ build-codec-linux-make:
    # need to use the "|| exit $?" suffix to get the allowed_failure return code, otherwise the job fails with code 1...<
    - ci/check_for_warnings.py $BUILD_OUTPUT || exit $?

build-unittests-linux:
  extends:
    - .build-job-with-check-for-warnings
    - .rules-basis
  script:
    - *print-common-info
    - make unittests -j 2>&1 | tee $BUILD_OUTPUT
    # need to use the "|| exit $?" suffix to get the allowed_failure return code, otherwise the job fails with code 1...<
    - ci/check_for_warnings.py $BUILD_OUTPUT || exit $?

build-td-object-renderer-standalone-linux:
  extends:
    - .build-job-with-check-for-warnings
    - .rules-basis
  script:
    - *print-common-info
    - make -C scripts/td_object_renderer/object_renderer_standalone -j 2>&1 | tee $BUILD_OUTPUT
    # need to use the "|| exit $?" suffix to get the allowed_failure return code, otherwise the job fails with code 1...<
    - ci/check_for_warnings.py $BUILD_OUTPUT || exit $?

build-codec-linux-cmake:
  extends:
    - .build-job-with-check-for-warnings
@@ -281,7 +261,7 @@ codec-smoke-test:
    expose_as: "Smoke test results"

# code selftest testvectors with memory-sanitizer binaries
msan-on-merge-request-linux:
codec-msan:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -300,10 +280,10 @@ msan-on-merge-request-linux:
    paths:
      - scripts/ref/logs/
      - test_output.txt
    expose_as: "Msan selftest results"
    expose_as: "msan selftest results"

# code selftest testvectors with address-sanitizer binaries
asan-on-merge-request-linux:
codec-asan:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -322,10 +302,10 @@ asan-on-merge-request-linux:
    paths:
      - scripts/ref/logs/
      - test_output.txt
    expose_as: "Asan selftest results"
    expose_as: "asan selftest results"

# test external renderer executable
external-renderer-make-pytest:
# test renderer executable
renderer-smoke-test:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -333,21 +313,19 @@ external-renderer-make-pytest:
  stage: test
  script:
    - make -j IVAS_rend
    - make -j unittests
    - make -j --directory scripts/td_object_renderer/object_renderer_standalone
    - python3 -m pytest -q -n auto -rA --junit-xml=report-junit.xml tests/renderer/test_renderer.py
  artifacts:
    name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results"
    when: always
    paths:
      - report-junit.xml
    expose_as: "external renderer make pytest results"
    expose_as: "renderer make pytest results"
    reports:
      junit:
        - report-junit.xml

# test external renderer executable with cmake + asan
external-renderer-cmake-asan-pytest:
# test renderer executable with cmake + asan
renderer-asan:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -364,13 +342,13 @@ external-renderer-cmake-asan-pytest:
    when: always
    paths:
      - report-junit.xml
    expose_as: "external renderer cmake asan pytest results"
    expose_as: "renderer asan pytest results"
    reports:
      junit:
        - report-junit.xml

# test external renderer executable with cmake + msan
external-renderer-cmake-msan-pytest:
# test renderer executable with cmake + msan
renderer-msan:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -387,35 +365,13 @@ external-renderer-cmake-msan-pytest:
    when: always
    paths:
      - report-junit.xml
    expose_as: "external renderer cmake msan pytest results"
    reports:
      junit:
        - report-junit.xml

# test external renderer executable with cmake vs decoder renderer
# TODO @tmu @knj @sgi -> converted to script, decide whether to re-enable later
.external-renderer-cmake-vs-decoder-pytest:
  extends:
    - .test-job-linux
    - .rules-merge-request
  needs: ["build-codec-linux-cmake"]
  stage: test
  script:
    - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DDEC_TO_REND_FLOAT_DUMP=true
    - cmake --build cmake-build -- -j
    - python3 -m pytest -q -n 1 -rA --junit-xml=report-junit.xml tests/renderer/test_renderer_vs_decoder.py
  artifacts:
    name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results"
    when: always
    paths:
      - report-junit.xml
    expose_as: "external renderer cmake vs decoder results"
    expose_as: "renderer msan pytest results"
    reports:
      junit:
        - report-junit.xml

# compare external renderer bitexactness between target and source branch
external-renderer-pytest-on-merge-request:
# compare renderer bitexactness between target and source branch
renderer-pytest-on-merge-request:
  extends:
    - .test-job-linux
    - .rules-merge-request
@@ -463,7 +419,7 @@ external-renderer-pytest-on-merge-request:
    paths:
      - report-junit.xml
      - report.html
    expose_as: "pytest external renderer results"
    expose_as: "pytest renderer results"
    reports:
      junit:
        - report-junit.xml
@@ -928,7 +884,7 @@ coverage-test-on-main-scheduled:
  script:
    - *print-common-info
    - make GCOV=1 -j
    - cp IVAS_rend IVAS_rend_ref # Copy exec to be able to run external renderer script  
    - cp IVAS_rend IVAS_rend_ref # Copy exec to be able to run renderer script  
    - python3 tests/create_short_testvectors.py
    - python3 -m pytest tests -v -n 0 --update_ref 1 -m create_ref --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec
    - python3 -m pytest tests -v -n 0 --update_ref 1 -m create_ref_part2 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec
@@ -937,7 +893,8 @@ coverage-test-on-main-scheduled:
    - python3 -m pytest -q -n auto tests/renderer/test_renderer_be_comparison.py
    - bash ci/ivas_voip_be_test.sh coverage
    - lcov -c -d obj -o coverage.info
    - genhtml coverage.info -o coverage
    - commit_sha=$(git rev-parse HEAD)
    - genhtml coverage.info -o coverage -t "Coverage on main @ $commit_sha"
  artifacts:
    name: "main-coverage-sha-$CI_COMMIT_SHORT_SHA"
    when: always
+0 −14
Original line number Diff line number Diff line
@@ -108,10 +108,6 @@ if(WMOPS)
  add_definitions("-DWMOPS=1")
endif()

if(DEC_TO_REND_FLOAT_DUMP)
  add_compile_definitions(DEC_TO_REND_FLOAT_DUMP)
endif()

project(stereo-evs)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) # make Visual Studio projects look nicer
include(CTest)
@@ -156,14 +152,6 @@ file(GLOB libUtilSrcs "lib_util/*.c")
file(GLOB libUtilHeaders "lib_util/*.h")
add_library(lib_util ${libUtilSrcs} ${libUtilHeaders})

file(GLOB unitTestCRendSrcs "scripts/ivas_pytests/tests/unit_tests/crend/*.c")
file(GLOB unitTestCRendHeaders "scripts/ivas_pytests/tests/unit_tests/crend/*.h")
add_executable(IVAS_crend_unit_test ${unitTestCRendSrcs} ${unitTestCRendHeaders})
target_link_libraries(IVAS_crend_unit_test lib_rend lib_dec lib_util lib_com lib_debug)

add_executable(renderer_standalone "scripts/td_object_renderer/object_renderer_standalone/object_renderer_standalone/renderer_standalone.c")
target_link_libraries(renderer_standalone lib_rend lib_dec lib_util lib_com lib_debug)

add_executable(IVAS_cod apps/encoder.c)
target_link_libraries(IVAS_cod lib_enc lib_util)
if(WIN32)
@@ -184,8 +172,6 @@ if(COPY_EXECUTABLES_FROM_BUILD_DIR)
  add_custom_command(TARGET IVAS_cod POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:IVAS_cod>" "${CMAKE_CURRENT_SOURCE_DIR}/")
  add_custom_command(TARGET IVAS_dec POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:IVAS_dec>" "${CMAKE_CURRENT_SOURCE_DIR}/")
  add_custom_command(TARGET IVAS_rend POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:IVAS_rend>" "${CMAKE_CURRENT_SOURCE_DIR}/")
  add_custom_command(TARGET IVAS_crend_unit_test POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:IVAS_crend_unit_test>" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/ivas_pytests/tests/unit_tests/crend/")
  add_custom_command(TARGET renderer_standalone POST_BUILD VERBATIM COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:renderer_standalone>" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/td_object_renderer/object_renderer_standalone/")
endif()

# Allow creating packages for CMake install
+1 −28
Original line number Diff line number Diff line
@@ -11,15 +11,6 @@ SRC_APP = apps
BUILD        = build
OBJDIR       = obj

# Dirs for python unittests
UTESTS_SCRIPT_DIR = scripts/ivas_pytests
UTESTS_DIR   = $(UTESTS_SCRIPT_DIR)/tests/unit_tests
UTESTS_CREND_DIR = $(UTESTS_DIR)/crend

# Source paths for python unittests 
SRC_UTESTS = $(UTESTS_CREND_DIR)


SRC_DIRS  = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBREND) $(SRC_LIBUTIL) $(SRC_APP) $(SRC_UTESTS)) 

# Name of CLI binaries
@@ -33,8 +24,6 @@ LIB_LIBENC ?= libivasenc.a
LIB_LIBREND  ?= libivasrend.a
LIB_LIBUTIL  ?= libivasutil.a

CLI_UTESTS_CREND ?= IVAS_crend_unit_test

# Default tool settings
CC        ?= gcc
RM        ?= rm -f
@@ -131,8 +120,6 @@ SRCS_LIBENC = $(foreach DIR,$(SRC_LIBENC),$(patsubst $(DIR)/%,%,$(wildcard $(D
SRCS_LIBREND  = $(foreach DIR,$(SRC_LIBREND),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
SRCS_LIBUTIL  = $(foreach DIR,$(SRC_LIBUTIL),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))

SRCS_UTESTS_CREND  = $(foreach DIR,$(UTESTS_CREND_DIR),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))

OBJS_LIBCOM      = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.o))
OBJS_LIBDEBUG    = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEBUG:.c=.o))
OBJS_LIBDEC      = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEC:.c=.o))
@@ -143,11 +130,6 @@ OBJS_CLI_APIDEC = $(OBJDIR)/decoder.o
OBJS_CLI_APIENC  = $(OBJDIR)/encoder.o
OBJS_CLI_APPREND = $(OBJDIR)/renderer.o

OBJS_CLI_UTESTS_CREND = $(addprefix $(OBJDIR)/,$(SRCS_UTESTS_CREND:.c=.o))

OBJS_UTESTS = $(OBJS_CLI_UTESTS_CREND)


DEPS = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.P) $(SRCS_LIBDEBUG:.c=.P) $(SRCS_LIBDEC:.c=.P) \
  $(SRCS_LIBENC:.c=.P) $(SRCS_LIBUTIL:.c=.P))

@@ -187,23 +169,14 @@ $(CLI_APIDEC): $(OBJS_CLI_APIDEC) $(LIB_LIBDEC) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(L
$(CLI_APIREND): $(OBJS_CLI_APPREND) $(LIB_LIBREND) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG) $(LIB_LIBDEC)
	$(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APPREND) -L. -livasrend -livasdec -livasutil -livasdebug -livascom $(LDLIBS) -o $(CLI_APIREND)

$(CLI_UTESTS_CREND): $(OBJS_CLI_UTESTS_CREND) $(LIB_LIBDEC) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(LIB_LIBDEBUG)
	$(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_UTESTS_CREND) -L. -livasdec -livascom -livasutil -livasdebug $(LDLIBS) -o $(UTESTS_CREND_DIR)/$(CLI_UTESTS_CREND)

unittests: $(CLI_UTESTS_CREND)

libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBREND) $(LIB_LIBUTIL)

clean: clean_unittests
clean: 
	$(QUIET)$(RM) $(OBJS_LIBENC) $(OBJS_LIBDEC) $(DEPS)
	$(QUIET)$(RM) $(DEPS:.P=.d)
	$(QUIET)test ! -d $(OBJDIR) || rm -rf $(OBJDIR)
	$(QUIET)$(RM) $(CLI_APIENC) $(CLI_APIDEC) $(CLI_APIREND) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL) $(LIB_LIBREND)

clean_unittests:
	$(QUIET)$(RM) $(OBJS_UTESTS)
	$(QUIET)$(RM) $(UTESTS_CREND_DIR)/$(CLI_UTESTS_CREND)

$(OBJDIR)/%.o : %.c | $(OBJDIR)
	$(QUIET_CC)$(CC) $(CFLAGS) -c -MD -o $@ $<
	@cp $(OBJDIR)/$*.d $(OBJDIR)/$*.P; \
+211 −30
Original line number Diff line number Diff line
@@ -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 )
            {
@@ -779,10 +832,6 @@ int main(
        fprintf( stderr, "Failed to open file: %s\n", args.outputFilePath );
        exit( -1 );
    }
#ifdef DEC_TO_REND_FLOAT_DUMP
    printf( "Warning: Renderer executable built with DEC_TO_REND_FLOAT_DUMP enabled!\n" );
    printf( "         Float dump file (./float_out.wav) will be forced as input.\n" );
#endif

    inBufferSize = frameSize_smpls * totalNumInChannels;
    outBufferSize = frameSize_smpls * numOutChannels;
@@ -834,20 +883,6 @@ int main(

        /* Convert from int to float and from interleaved to packed */
        convertInputBuffer( inpInt16Buffer, numSamplesRead, frameSize_smpls, num_in_channels, inFloatBuffer );
#ifdef DEC_TO_REND_FLOAT_DUMP
        /* Overwrite from dump file */
        float tmp[960 * 16];
        dbgread( tmp, sizeof( float ), numSamplesRead, "./float_out.raw" );

        /* Conversion from interleaved to packed still necessary */
        for ( int32_t i = 0; i < numSamplesRead / num_in_channels; ++i )
        {
            for ( int32_t c = 0; c < num_in_channels; ++c )
            {
                inFloatBuffer[c * frameSize_smpls + i] = tmp[i * num_in_channels + c];
            }
        }
#endif


        ObjectPositionBuffer mtdBuffer;
@@ -1065,6 +1100,22 @@ static IVAS_REND_AudioConfig ambisonicsOrderToEnum(
    return IVAS_REND_AUDIO_CONFIG_UNKNOWN;
}

#ifdef FIX_293_EXT_RENDERER_CLI
static const CmdLnParser_Option *findOptionById(
    const int32_t id )
{
    for ( int32_t i = 0; i < numCliOptions; ++i )
    {
        if ( cliOptions[i].id == id )
        {
            return &cliOptions[i];
        }
    }

    return NULL;
}
#endif

static bool parseInConfig(
    const char *inFormatStr,
    InputConfig *inConfig,
@@ -1152,8 +1203,13 @@ static bool parseInConfig(

                if ( error == IVAS_ERR_FAILED_FILE_OPEN )
                {
#ifdef FIX_293_EXT_RENDERER_CLI
                    /* Failed to open with given string - most likely wasn't a file path */
                    const CmdLnParser_Option *listOption = findOptionById( CmdLnOptionId_listFormats );
                    fprintf( stderr, "Unsupported input format: %s. To list valid formats, use option --%s.\n", inFormatStr, listOption->match );
#else
                    fprintf( stderr, "Unsupported input format: %s\n", inFormatStr );
#endif
                    return false;
                }
                if ( error != IVAS_ERR_OK )
@@ -1168,10 +1224,20 @@ static bool parseInConfig(
            }
            break;
        default:
#ifdef FIX_293_EXT_RENDERER_CLI
        {
            /* Default case covers formats that are defined in the IVAS_REND_AudioConfig enum,
             * but cannot be used at input, e.g. BINAURAL */
            const CmdLnParser_Option *listOption = findOptionById( CmdLnOptionId_listFormats );
            fprintf( stderr, "Unsupported input format: %s. To list valid formats, use option --%s.\n", inFormatStr, listOption->match );
            return false;
        }
#else
            /* Default case covers formats that are defined in the IVAS_REND_AudioConfig enum,
             * but cannot be used at input, e.g. BINAURAL */
            fprintf( stderr, "Unsupported input format: %s\n", inFormatStr );
            return false;
#endif
    }

    return true;
@@ -1337,7 +1403,20 @@ static IVAS_REND_AudioConfig parseAudioConfig(
    }
    if ( strncmp( charBuf, "ISM", 3 ) == 0 )
    {
#ifdef FIX_293_EXT_RENDERER_CLI
        /* Accept input config as ISM only if the 4th character is a number from 1 to 4.
         * Otherwise, do nothing. Unknown audio config will be returned. */
        switch ( charBuf[3] )
        {
            case '1':
            case '2':
            case '3':
            case '4':
                return IVAS_REND_AUDIO_CONFIG_OBJECT;
        }
#else
        return IVAS_REND_AUDIO_CONFIG_OBJECT;
#endif
    }
    if ( strncmp( charBuf, "MASA", 4 ) == 0 )
    {
@@ -1366,7 +1445,7 @@ static IVAS_REND_AudioConfig parseAudioConfig(
}

#ifdef REND_CFG_LFE
static bool parseLfeConfig(
static bool parseLfePositionConfig(
    const char *value,
    float *lfeGain,
    float *lfeAzimuth,
@@ -1413,6 +1492,7 @@ static bool parseLfeConfig(
}
#endif

#ifndef FIX_293_EXT_RENDERER_CLI
static const CmdLnParser_Option *findOptionById(
    const int32_t id )
{
@@ -1426,6 +1506,7 @@ static const CmdLnParser_Option *findOptionById(

    return NULL;
}
#endif

static bool checkRequiredArgs(
    CmdlnArgs args )
@@ -1511,10 +1592,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 +1692,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;
@@ -1744,6 +1836,24 @@ void IsmPositionProvider_getNextFrame(

    for ( objIdx = 0; objIdx < positionProvider->numObjects; ++objIdx )
    {
#ifdef FIX_293_EXT_RENDERER_CLI
        /* If ISM metadata reader is open, read from metadata file */
        if ( positionProvider->ismReaders[objIdx] != NULL )
        {
            getMetadataFromFileReader( positionProvider->ismReaders[objIdx], objectMetadataBuffer, objIdx );
        }
        /* Otherwise, if positions were provided in a scene description file, use them */
        else if ( positionProvider->positions[objIdx] != NULL )
        {
            readFromShorthandMetadata( positionProvider, objectMetadataBuffer, objIdx );
        }
        /* Otherwise fall back to default position */
        else
        {
            objectMetadataBuffer->positions[objIdx].azimuth = 0.0f;
            objectMetadataBuffer->positions[objIdx].elevation = 0.0f;
        }
#else
        if ( positionProvider->ismReaders[objIdx] != NULL )
        {
            getMetadataFromFileReader( positionProvider->ismReaders[objIdx], objectMetadataBuffer, objIdx );
@@ -1752,6 +1862,7 @@ void IsmPositionProvider_getNextFrame(
        {
            readFromShorthandMetadata( positionProvider, objectMetadataBuffer, objIdx );
        }
#endif

        /* Wrap azimuth to lie within (-180, 180] range */
        while ( objectMetadataBuffer->positions[objIdx].azimuth < 0.0f )
@@ -2139,7 +2250,11 @@ static void parseMasa(
    if ( strncmp( line, "MASA", 4 ) != 0 )
    {
        char numTcs = *line;
#ifdef FIX_293_EXT_RENDERER_CLI
        snprintf( line, 6 /* write at most 6 characters: MASAx + null termination */, "MASA%c", numTcs );
#else
        sprintf( line, "MASA%c", numTcs );
#endif
    }

    inConfig->masaBuses[idx].audioConfig = parseAudioConfig( line );
@@ -2381,6 +2496,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 )
Loading