From c9e198eaf3c23a827816e47116d83cd83e955405 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 27 Nov 2025 14:31:26 +0100 Subject: [PATCH 01/34] add proper return and printout for command-line arguments --- apps/isar_post_rend.c | 123 +++++++++++++++++++++++++++------------- apps/renderer.c | 4 +- lib_util/cmdln_parser.c | 30 +++++++--- lib_util/cmdln_parser.h | 2 +- 4 files changed, 109 insertions(+), 50 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 96dc9b827f..c5d8605970 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -159,7 +159,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputMetadata, .match = "input_metadata", .matchShort = "im", - .description = "Space-separated list of path to metadata files for BINAURAL_SPLIT_PCM input mode", + .description = "Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode", }, { .id = CmdLnOptionId_outputFile, @@ -183,13 +183,13 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_SplitRendBFIFile, .match = "post_rend_bfi_file", .matchShort = "prbfi", - .description = "Split rendering option: bfi file", + .description = "Split rendering BFI (Bad Frame Indicator) file", }, { .id = CmdLnOptionId_noDelayCmp, .match = "no_delay_compensation", .matchShort = "no_delay_cmp", - .description = "[flag] Turn off delay compensation", + .description = "Turn off delay compensation", }, { .id = CmdLnOptionId_complexityLevel, @@ -201,7 +201,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_quietModeEnabled, .match = "quiet", .matchShort = "q", - .description = "[flag] Limit printouts to terminal", + .description = "Quiet mode - limit printouts to terminal", }, { .id = CmdLnOptionId_listFormats, @@ -213,7 +213,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_framing, .match = "framing", .matchShort = "fr", - .description = "Set Render audio framing.", + .description = "Set audio rendering frame size", }, #ifdef RTP_S4_251135_CR26253_0016_REV1 { @@ -349,7 +349,7 @@ static bool parseInConfig( /* Default case covers formats that are defined in the AUDIO_CONFIG 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 ); + fprintf( stderr, "Error: Unsupported input format: %s. To list valid formats, use option --%s.\n", inFormatStr, listOption->match ); return false; } } @@ -440,7 +440,7 @@ static bool checkRequiredArgs( if ( isEmptyString( args.inputFilePath ) ) { tmpOption = findOptionById( CmdLnOptionId_inputFile ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; } @@ -450,19 +450,13 @@ static bool checkRequiredArgs( { /* Neither scene description input nor single-type input was specified on command line */ tmpOption = findOptionById( CmdLnOptionId_inputFormat ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; } if ( isEmptyString( args.outputFilePath ) ) { tmpOption = findOptionById( CmdLnOptionId_outputFile ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - if ( args.sampleRate == 0 ) - { - tmpOption = findOptionById( CmdLnOptionId_sampleRate ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); + fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; } if ( missingRequiredArg ) @@ -508,7 +502,7 @@ static CmdlnArgs defaultArgs( } -static void parseOption( +static int16_t parseOption( const int32_t optionId, char **optionValues, const int16_t numOptionValues, @@ -519,22 +513,34 @@ static void parseOption( switch ( optionId ) { case CmdLnOptionId_listFormats: - assert( numOptionValues == 0 ); printSupportedAudioConfigs(); exit( 0 ); case CmdLnOptionId_inputFile: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No input file has been provided!\n" ); + return -1; + } strncpy( args->inputFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_inputFormat: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No input file format has been provided!\n" ); + return -1; + } if ( !parseInConfig( optionValues[0], &args->inConfig, &args->sceneDescriptionInput ) ) { - exit( -1 ); /* Error printout handled by failing function */ + /* Error printout handled by failing function */ + return -1; } break; case CmdLnOptionId_inputMetadata: - assert( numOptionValues <= RENDERER_MAX_ISAR_MD_INPUTS ); + if ( numOptionValues < 1 || numOptionValues > RENDERER_MAX_ISAR_MD_INPUTS ) + { + fprintf( stderr, "Error: No paths to metadata files for BINAURAL_SPLIT_PCM input mode has been provided!\n" ); + return -1; + } for ( int16_t i = 0; i < numOptionValues; ++i ) { strncpy( args->inMetadataFilePaths[i], optionValues[i], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); @@ -542,68 +548,105 @@ static void parseOption( args->numInMetadataFiles = numOptionValues; break; case CmdLnOptionId_outputFile: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No output file has been provided!\n" ); + return -1; + } strncpy( args->outputFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_sampleRate: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No input sampling rate has been provided!\n" ); + return -1; + } args->sampleRate = (int32_t) ( strtol( optionValues[0], NULL, 10 ) * 1000 ); if ( args->sampleRate == 0 ) { - fprintf( stderr, "Invalid sampling rate specified\n" ); - exit( -1 ); + fprintf( stderr, "Error: Invalid sampling rate specified\n" ); + return -1; } break; case CmdLnOptionId_trajFile: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No head rotation trajectory file has been provided!\n" ); + return -1; + } strncpy( args->headRotationFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_SplitRendBFIFile: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No bfi file has been provided!\n" ); + return -1; + } strncpy( args->splitRendBFIFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_complexityLevel: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No complexity level has been provided!\n" ); + return -1; + } args->complexityLevel = (int32_t) ( strtol( optionValues[0], NULL, 10 ) ); if ( args->complexityLevel < ISAR_POST_REND_COMPLEXITY_LEVEL_ONE || args->complexityLevel > ISAR_POST_REND_COMPLEXITY_LEVEL_THREE ) { - fprintf( stdout, "Invalid complexity level specified.\n" ); - exit( -1 ); + fprintf( stderr, "Error: Invalid complexity level specified.\n" ); + return -1; } else if ( args->complexityLevel == ISAR_POST_REND_COMPLEXITY_LEVEL_ONE || args->complexityLevel == ISAR_POST_REND_COMPLEXITY_LEVEL_TWO ) { - fprintf( stdout, "Complexity levels 1 and 2 will be defined after characterisation - default to level 3 (full functionality).\n" ); + fprintf( stderr, "Complexity levels 1 and 2 will be defined after characterisation - default to level 3 (full functionality).\n" ); } break; case CmdLnOptionId_noDelayCmp: - assert( numOptionValues == 0 ); + if ( numOptionValues != 0 ) + { + fprintf( stderr, "Error: Incorrect specification of the `-%s/--%s` command-line option!\n", cliOptions[optionId].matchShort, cliOptions[optionId].match ); + return -1; + } args->delayCompensationEnabled = false; break; case CmdLnOptionId_quietModeEnabled: - assert( numOptionValues == 0 ); + if ( numOptionValues != 0 ) + { + fprintf( stderr, "Error: Incorrect specification of the `-%s/--%s` command-line option!\n", cliOptions[optionId].matchShort, cliOptions[optionId].match ); + return -1; + } args->quietModeEnabled = true; break; case CmdLnOptionId_framing: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: Incorrect audio rendering frame size!\n" ); + return -1; + } if ( !parseRenderFramesize( optionValues[0], &args->render_framesize ) ) { - fprintf( stderr, "Unknown or invalid option for frame size: %s\n", optionValues[0] ); - exit( -1 ); + fprintf( stderr, "Error: Unknown or invalid option for audio rendring frame size: %s\n", optionValues[0] ); + return -1; } break; #ifdef RTP_S4_251135_CR26253_0016_REV1 case CmdLnOptionId_srParamsFile: - assert( numOptionValues == 1 ); + if ( numOptionValues != 1 ) + { + fprintf( stderr, "Error: No path to split rending init params file has been provided!\n" ); + return -1; + } strncpy( args->srParamsFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; #endif default: - assert( 0 && "This should be unreachable - all command line options should be explicitly handled." ); + fprintf( stderr, "Error: Incorrect or invalid command-line usage!\n" ); + return -1; break; } - return; + return 0; } @@ -639,7 +682,7 @@ static void printSupportedAudioConfigs( void ) #endif }; - fprintf( stdout, "Supported audio formats:\n" ); + fprintf( stdout, "Supported audio formats:\n\n" ); for ( i = 0; i < sizeof( supportedFormats ) / sizeof( *supportedFormats ); i++ ) { fprintf( stdout, "%s\n", supportedFormats[i] ); diff --git a/apps/renderer.c b/apps/renderer.c index 753a116c15..1e525e454c 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -2750,7 +2750,7 @@ static CmdlnArgs defaultArgs( return args; } -static void parseOption( +static int16_t parseOption( const int32_t optionId, char **optionValues, const int16_t numOptionValues, @@ -2946,7 +2946,7 @@ static void parseOption( break; } - return; + return 0; } static CmdlnArgs parseCmdlnArgs( diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 1b9eb7a928..0fa9bd1aeb 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -179,7 +179,8 @@ static int16_t parseOpts( Option *opts, const int32_t numOpts, void *pOutputStruct, - CmdLnParser_FnPtr_ParseOption parseOption ) + CmdLnParser_FnPtr_ParseOption parseOption +) { Option *currOpt = NULL; int32_t currOptIdx = 1; @@ -238,7 +239,10 @@ static int16_t parseOpts( { if ( currOpt != NULL ) { - parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); + if ( parseOption(currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct) != 0 ) + { + return -1; + } currOpt->hasBeenParsed = 1; } @@ -253,7 +257,10 @@ static int16_t parseOpts( /* Parse last option */ if ( currOpt != NULL ) { - parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); + if (parseOption(currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct) != 0) + { + return -1; + } currOpt->hasBeenParsed = 1; } @@ -330,28 +337,36 @@ static void printUsage( const OptionProps *optionProps, const int32_t numOptions ) { + int32_t optNameLength; + fprintf( stderr, "\n" ); fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) ); fprintf( stderr, "\n" ); - fprintf( stderr, "Valid options:\n" ); /* Find option with longest name, used for pretty formatting */ int32_t maxOptNameLength = 0; for ( int32_t i = 0; i < numOptions; ++i ) { - const int32_t optNameLength = totalOptionNameLength( optionProps[i] ); + optNameLength = totalOptionNameLength( optionProps[i] ); if ( maxOptNameLength < optNameLength ) { maxOptNameLength = optNameLength; } } + fprintf( stderr, "Mandatory parameters:\n" ); + const int32_t preDescriptionWhitespace = 8; const int32_t leftColumnAdditionalChars = 7; for ( int32_t i = 0; i < numOptions; ++i ) { OptionProps opt = optionProps[i]; - const int32_t optNameLength = totalOptionNameLength( optionProps[i] ); + optNameLength = totalOptionNameLength( optionProps[i] ); + + if (i == 2) + { + fprintf( stderr, "\nOptional parameters:\n" ); + } /* TODO(sgi): make matchShort optional */ fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); @@ -369,7 +384,8 @@ int16_t CmdLnParser_parseArgs( const OptionProps *optionProps, const int32_t numOptions, void *pOutputStruct, - CmdLnParser_FnPtr_ParseOption parseOption ) + CmdLnParser_FnPtr_ParseOption parseOption +) { assert( numOptions <= MAX_SUPPORTED_OPTS ); diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index 2627d55662..fe868cdcf8 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -45,7 +45,7 @@ typedef struct } CmdLnParser_Option; /* Function for parsing option values into an output struct, to be implemented by the user */ -typedef void ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, char **optionValues, int16_t numOptionValues, void *pOutputStruct ); +typedef int16_t ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, char **optionValues, int16_t numOptionValues, void *pOutputStruct ); int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption ); -- GitLab From 03ca34de6a082a1c6359e77e5b49f17be5c3fe34 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 27 Nov 2025 16:20:27 +0100 Subject: [PATCH 02/34] distinguish mandatory and optional parameters --- apps/isar_post_rend.c | 28 ++++++++++++++++++++-------- apps/renderer.c | 9 ++++++--- lib_util/cmdln_parser.c | 18 ++++++++++-------- lib_util/cmdln_parser.h | 4 ++-- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index c5d8605970..bc6e661a40 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -60,6 +60,7 @@ *------------------------------------------------------------------------------------------*/ #define POST_REND_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) +#define POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS 3 #define ISAR_MAX16B_FLT 32767.0f #define ISAR_MIN16B_FLT ( -32768.0f ) @@ -120,6 +121,7 @@ typedef struct bool quietModeEnabled; bool sceneDescriptionInput; IVAS_RENDER_FRAMESIZE render_framesize; + int16_t numMandatoryCmdLineParams; } CmdlnArgs; typedef enum @@ -155,18 +157,18 @@ static const CmdLnParser_Option cliOptions[] = { .matchShort = "if", .description = "Audio format of input file (e.g. BINAURAL_SPLIT_PCM, use -l for a list)", }, - { - .id = CmdLnOptionId_inputMetadata, - .match = "input_metadata", - .matchShort = "im", - .description = "Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode", - }, { .id = CmdLnOptionId_outputFile, .match = "output_file", .matchShort = "o", .description = "Path to the output file", }, + { + .id = CmdLnOptionId_inputMetadata, + .match = "input_metadata", + .matchShort = "im", + .description = "Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode", + }, { .id = CmdLnOptionId_sampleRate, .match = "sample_rate", @@ -453,15 +455,17 @@ static bool checkRequiredArgs( fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; } + if ( isEmptyString( args.outputFilePath ) ) { tmpOption = findOptionById( CmdLnOptionId_outputFile ); fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; } + if ( missingRequiredArg ) { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions ); + CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, args.numMandatoryCmdLineParams ); } return !missingRequiredArg; @@ -481,6 +485,12 @@ static CmdlnArgs defaultArgs( #endif args.sampleRate = 0; + args.inConfig.numBinBuses = 0; + args.inConfig.binBuses[0].srRtp = false; + args.inConfig.binBuses[0].audioConfig = IVAS_AUDIO_CONFIG_INVALID; + args.inConfig.binBuses[0].inputChannelIndex = 0; + args.inConfig.binBuses[0].gain_dB = 0; + args.outConfig.audioConfig = IVAS_AUDIO_CONFIG_INVALID; for ( int32_t i = 0; i < RENDERER_MAX_ISAR_MD_INPUTS; ++i ) @@ -492,11 +502,13 @@ static CmdlnArgs defaultArgs( clearString( args.headRotationFilePath ); clearString( args.splitRendBFIFilePath ); + args.complexityLevel = ISAR_POST_REND_COMPLEXITY_LEVEL_THREE; args.delayCompensationEnabled = true; args.quietModeEnabled = false; args.sceneDescriptionInput = false; args.render_framesize = IVAS_RENDER_FRAMESIZE_20MS; + args.numMandatoryCmdLineParams = POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS; return args; } @@ -656,7 +668,7 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption ) != 0 ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, args.numMandatoryCmdLineParams ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } diff --git a/apps/renderer.c b/apps/renderer.c index 1e525e454c..35185722fc 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -70,6 +70,7 @@ #define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) #define RENDERER_MAX_METADATA_LENGTH 8192 #define RENDERER_MAX_METADATA_LINE_LENGTH 1024 +#define RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS 1000 #define IVAS_MAX16B_FLT 32767.0f #define IVAS_MIN16B_FLT ( -32768.0f ) @@ -203,6 +204,7 @@ typedef struct #ifdef FIX_1318_ROOM_SIZE_CMD_LINE IVAS_ROOM_SIZE_T reverbRoomSize; #endif + int16_t numMandatoryCmdLineParams; } CmdlnArgs; typedef enum @@ -2663,7 +2665,7 @@ static bool checkRequiredArgs( } if ( missingRequiredArg ) { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions ); + CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, 100 ); } return !missingRequiredArg; @@ -2743,10 +2745,11 @@ static CmdlnArgs defaultArgs( args.aeSequence.selected = 0; args.aeSequence.frameCounter = 0; #ifdef FIX_1318_ROOM_SIZE_CMD_LINE - args.reverbRoomSize = IVAS_ROOM_SIZE_AUTO; #endif + args.numMandatoryCmdLineParams = RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS; + return args; } @@ -2955,7 +2958,7 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption ) != 0 ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, args.numMandatoryCmdLineParams ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 0fa9bd1aeb..02d59ec600 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -335,7 +335,8 @@ static void printOptDescriptionAligned( static void printUsage( const char *argv0, const OptionProps *optionProps, - const int32_t numOptions ) + const int32_t numOptions, + const int16_t numMandatoryCmdLineParams ) { int32_t optNameLength; @@ -363,9 +364,9 @@ static void printUsage( OptionProps opt = optionProps[i]; optNameLength = totalOptionNameLength( optionProps[i] ); - if (i == 2) + if ( i == numMandatoryCmdLineParams ) { - fprintf( stderr, "\nOptional parameters:\n" ); + fprintf( stderr, "\nOptions:\n" ); } /* TODO(sgi): make matchShort optional */ @@ -384,8 +385,8 @@ int16_t CmdLnParser_parseArgs( const OptionProps *optionProps, const int32_t numOptions, void *pOutputStruct, - CmdLnParser_FnPtr_ParseOption parseOption -) + CmdLnParser_FnPtr_ParseOption parseOption, + const int16_t numMandatoryCmdLineParams ) { assert( numOptions <= MAX_SUPPORTED_OPTS ); @@ -405,16 +406,17 @@ int16_t CmdLnParser_parseArgs( return 0; fail: - printUsage( argv[0], optionProps, numOptions ); + printUsage( argv[0], optionProps, numOptions, numMandatoryCmdLineParams ); return -1; } void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, - const int32_t numOptions ) + const int32_t numOptions, + const int16_t numMandatoryCmdLineParams ) { - printUsage( executableName, options, numOptions ); + printUsage( executableName, options, numOptions, numMandatoryCmdLineParams ); return; } diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index fe868cdcf8..58deb4a41e 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -47,8 +47,8 @@ typedef struct /* Function for parsing option values into an output struct, to be implemented by the user */ typedef int16_t ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, char **optionValues, int16_t numOptionValues, void *pOutputStruct ); -int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption ); +int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption, const int16_t numMandatoryCmdLineParams ); -void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions ); +void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions, const int16_t numMandatoryCmdLineParams ); #endif /* CMDLN_PARSER_H */ -- GitLab From 5ab7084b598b655b56bf1a8fc06df254e433ba19 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 27 Nov 2025 16:20:53 +0100 Subject: [PATCH 03/34] update readme for ISAR --- readme.txt | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/readme.txt b/readme.txt index 129393976b..98d2914fde 100644 --- a/readme.txt +++ b/readme.txt @@ -386,15 +386,25 @@ The usage of the "ISAR_post_rend" program is as follows: Usage: ISAR_post_rend [options] -Options: --------- +Mandatory parameters: +--------------------- + -i File : Input File (input file is bitstream if format is BINAURAL_SPLIT_CODED, or PCM/WAV file if format is BINAURAL_SPLIT_PCM) -if Format : Input Format of input (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM) --im File : Coded metadata File for BINAURAL_SPLIT_PCM input format -o File : Output Audio File in BINAURAL format --fs : Input sampling rate in kHz (48) --prbfi File : BFI File +Options: +-------- +-im Files : Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode +-fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs +-T File : Head rotation trajectory file for simulation of head tracking +-prbfi File : Split rendering BFI (Bad Frame Indicator) file +-no_delay_cmp : Turn off delay compensation +-level : Complexity level, level = (1, 2, 3), will be defined after characterisation. +-q : Quiet mode, limit printouts to terminal, default is deactivated +-l : List supported audio formats +-fr : Set audio rendering frame size +-s File : Path to the split rendering init params file The usage of the "ambi_converter" program is as follows: -------------------------------------------------------- -- GitLab From 6f9e6227995c6772e2de74c32341d695c3cd64fd Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Fri, 28 Nov 2025 13:45:01 +0100 Subject: [PATCH 04/34] improve error messages --- apps/isar_post_rend.c | 16 ++++++++-------- apps/renderer.c | 2 +- lib_util/split_render_file_read_write.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index bc6e661a40..fa5424f7bc 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -1052,13 +1052,13 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nCould not open split rend metadata file %s\n", args.inMetadataFilePaths[0] ); + fprintf( stderr, "\nError: Could not open split rendering metadata file %s!\n", args.inMetadataFilePaths[0] ); goto cleanup; } if ( AudioFileReader_open( &audioReader, audioFilePath ) != IVAS_ERR_OK ) { - fprintf( stderr, "\nError opening file: %s\n", audioFilePath ); + fprintf( stderr, "\nError: Could not open input file: %s!\n", audioFilePath ); goto cleanup; } } @@ -1077,7 +1077,7 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nCould not open split rend params file %s\n", args.srParamsFilePath ); + fprintf( stderr, "\nError: Could not open split rendering init params file %s!\n", args.srParamsFilePath ); goto cleanup; } @@ -1087,7 +1087,7 @@ int main( if ( ( error = IVAS_RTP_READER_Init( &srRTP, args.inputFilePath, NULL, false, NULL ) ) != IVAS_ERR_OK ) #endif { - fprintf( stderr, "error in IVAS_RTP_READER_Init(): %d\n", error ); + fprintf( stderr, "\nError in IVAS_RTP_READER_Init(): %d!\n", error ); goto cleanup; } audioReader = NULL; @@ -1113,7 +1113,7 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nCould not open split rend metadata file %s\n", args.inputFilePath ); + fprintf( stderr, "\nError reading input file %s, error code: %d!\n", args.inputFilePath, error ); goto cleanup; } audioReader = NULL; @@ -1139,14 +1139,14 @@ int main( /* else if sampling rate given on command line, compare with wav file */ else if ( inFileSampleRate != args.sampleRate ) { - fprintf( stderr, "\nSampling rate mismatch: %d Hz requested, but %d Hz found in file %s\n", args.sampleRate, inFileSampleRate, args.inputFilePath ); + fprintf( stderr, "\nError: Sampling rate mismatch: %d Hz requested, but %d Hz found in file %s!\n", args.sampleRate, inFileSampleRate, args.inputFilePath ); goto cleanup; } break; case IVAS_ERR_SAMPLING_RATE_UNKNOWN: /* Returned when input is raw PCM */ if ( args.sampleRate == 0 ) { - fprintf( stderr, "\nSampling rate must be specified on command line when using raw PCM input\n" ); + fprintf( stderr, "\nError: Sampling rate must be specified on command line in case of BINAURAL_SPLIT_PCM input!\n" ); goto cleanup; } break; @@ -1161,7 +1161,7 @@ int main( error = AudioFileReader_getNumChannels( audioReader, &inFileNumChannels ); if ( error != IVAS_ERR_OK && error != IVAS_ERR_NUM_CHANNELS_UNKNOWN ) { - fprintf( stderr, "\nAudioFileReader_getNumChannels failed: %s\n", ivas_error_to_string( error ) ); + fprintf( stderr, "\nError: AudioFileReader_getNumChannels() failed: %s\n", ivas_error_to_string( error ) ); goto cleanup; } } diff --git a/apps/renderer.c b/apps/renderer.c index 35185722fc..d960649959 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1474,7 +1474,7 @@ int main( 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", outFile ); + fprintf( stderr, "\nError: Could not open split rend metadata file %s!\n", outFile ); goto cleanup; } } diff --git a/lib_util/split_render_file_read_write.c b/lib_util/split_render_file_read_write.c index 59dad090fa..26ea6d8c81 100644 --- a/lib_util/split_render_file_read_write.c +++ b/lib_util/split_render_file_read_write.c @@ -76,13 +76,13 @@ ivas_error split_rend_reader_open( if ( ( hSplitRendFileReadWrite = (SplitFileReadWrite *) malloc( sizeof( SplitFileReadWrite ) ) ) == NULL ) { - return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for split rendering writer\n" ) ); + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for split rendering reader\n" ) ); } hSplitRendFileReadWrite->file = fopen( filename, "rb" ); if ( hSplitRendFileReadWrite->file == NULL ) { - return ( IVAS_ERROR( IVAS_ERR_FAILED_FILE_READ, "\nCould not open split rend metadata file %s\n", filename ) ); + return ( IVAS_ERROR( IVAS_ERR_FAILED_FILE_READ, "\nCould not open file %s\n", filename ) ); } header_len = strlen( header ); @@ -98,7 +98,7 @@ ivas_error split_rend_reader_open( if ( strncmp( header_read, header, header_len ) ) { - return ( IVAS_ERROR( IVAS_ERR_FAILED_FILE_READ, "\nError split rend bitstream main header mismatch\n" ) ); + return ( IVAS_ERROR( IVAS_ERR_FAILED_FILE_READ, "\nSplit rendering bitstream main header mismatch\n" ) ); } fread( &hSplitRendFileReadWrite->delay_ns, sizeof( uint32_t ), 1, hSplitRendFileReadWrite->file ); -- GitLab From 3654d34a8cc7c2cd4441f76118af098b07f94f25 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 3 Dec 2025 10:40:49 +0100 Subject: [PATCH 05/34] clang format --- apps/isar_post_rend.c | 42 +++++++++++++++++++++++++++++++------ apps/renderer.c | 6 +++--- lib_rend/ivas_output_init.c | 4 ++-- lib_util/cmdln_parser.c | 7 +++---- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 5cdb0930ce..877220cdba 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -57,7 +57,7 @@ * Local constants *------------------------------------------------------------------------------------------*/ -#define POST_REND_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) +#define POST_REND_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) #define POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS 3 #define ISAR_MAX16B_FLT 32767.0f @@ -116,6 +116,7 @@ typedef struct bool sceneDescriptionInput; IVAS_RENDER_FRAMESIZE render_framesize; int16_t numMandatoryCmdLineParams; + bool help; } CmdlnArgs; typedef enum @@ -134,6 +135,7 @@ typedef enum CmdLnOptionId_SplitRendBFIFile, CmdLnOptionId_framing, CmdLnOptionId_srParamsFile, + CmdLnOptionId_help, } CmdLnOptionId; static const CmdLnParser_Option cliOptions[] = { @@ -215,6 +217,12 @@ static const CmdLnParser_Option cliOptions[] = { .matchShort = "s", .description = "Path to the split rendering init params file", }, + { + .id = CmdLnOptionId_help, + .match = "help", + .matchShort = "h", + .description = "Show this help message and exit", + }, }; @@ -481,6 +489,7 @@ static CmdlnArgs defaultArgs( args.render_framesize = IVAS_RENDER_FRAMESIZE_20MS; args.numMandatoryCmdLineParams = POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS; + args.help = false; return args; } @@ -516,7 +525,7 @@ static int16_t parseOption( if ( !parseInConfig( optionValues[0], &args->inConfig, &args->sceneDescriptionInput ) ) { /* Error printout handled by failing function */ - return -1; + return -1; } break; case CmdLnOptionId_inputMetadata: @@ -621,6 +630,9 @@ static int16_t parseOption( } strncpy( args->srParamsFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; + case CmdLnOptionId_help: + args->help = true; + break; default: fprintf( stderr, "Error: Incorrect or invalid command-line usage!\n" ); return -1; @@ -642,6 +654,12 @@ static CmdlnArgs parseCmdlnArgs( exit( -1 ); /* Error printout handled by failing function */ } + if ( args.help ) + { + CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, args.numMandatoryCmdLineParams ); + exit( 0 ); + } + if ( !checkRequiredArgs( args ) ) { exit( -1 ); /* Error printout handled by failing function */ @@ -927,7 +945,6 @@ int main( convert_backslash( args.inputFilePath ); convert_backslash( args.outputFilePath ); - convert_backslash( args.headRotationFilePath ); convert_backslash( args.srParamsFilePath ); /*------------------------------------------------------------------------------------------* @@ -936,6 +953,7 @@ int main( if ( !isEmptyString( args.headRotationFilePath ) ) { + convert_backslash( args.headRotationFilePath ); if ( RotationFileReader_open( args.headRotationFilePath, &headRotReader ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError opening file: %s\n", args.headRotationFilePath ); @@ -950,7 +968,11 @@ int main( if ( !isEmptyString( args.splitRendBFIFilePath ) ) { convert_backslash( args.splitRendBFIFilePath ); - SplitRendBFIFileReader_open( args.splitRendBFIFilePath, &splitRendBFIReader ); + if ( SplitRendBFIFileReader_open(args.splitRendBFIFilePath, &splitRendBFIReader ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError opening file: %s\n", args.splitRendBFIFilePath ); + goto cleanup; + } } /*------------------------------------------------------------------------------------------* @@ -972,7 +994,15 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nError: Could not open split rendering metadata file %s!\n", args.inMetadataFilePaths[0] ); + if (isEmptyString(args.inMetadataFilePaths[0])) + { + const CmdLnParser_Option *listOption = findOptionById( CmdLnOptionId_inputMetadata ); + fprintf( stderr, "\nError: Split rendering metadata file not specified, use option -%s/--%s.\n", listOption->matchShort, listOption->match ); + } + else + { + fprintf( stderr, "\nError: Could not open split rendering metadata file %s!\n", args.inMetadataFilePaths[0] ); + } goto cleanup; } @@ -1000,7 +1030,7 @@ int main( if ( ( error = IVAS_RTP_READER_Init( &srRTP, args.inputFilePath, NULL, false, NULL ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "\nError in IVAS_RTP_READER_Init(): %d!\n", error ); + fprintf( stderr, "\nError in IVAS_RTP_READER_Init(), error code: %d!\n", error ); goto cleanup; } audioReader = NULL; diff --git a/apps/renderer.c b/apps/renderer.c index 130b7a1218..49b6dca223 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -65,9 +65,9 @@ * Local constants *------------------------------------------------------------------------------------------*/ -#define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) -#define RENDERER_MAX_METADATA_LENGTH 8192 -#define RENDERER_MAX_METADATA_LINE_LENGTH 1024 +#define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) +#define RENDERER_MAX_METADATA_LENGTH 8192 +#define RENDERER_MAX_METADATA_LINE_LENGTH 1024 #define RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS 1000 #define IVAS_MAX16B_FLT 32767.0f diff --git a/lib_rend/ivas_output_init.c b/lib_rend/ivas_output_init.c index 01e1d77231..0cafba8aef 100644 --- a/lib_rend/ivas_output_init.c +++ b/lib_rend/ivas_output_init.c @@ -552,11 +552,11 @@ ivas_error get_channel_config( } else if ( config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) { - strcpy( str, "BINAURAL_SPLIT_CODED" ); + strcpy( str, "Binaural: split-rendering mode using coded data" ); } else if ( config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { - strcpy( str, "Binaural_Split_PCM" ); + strcpy( str, "Binaural: split-rendering mode using raw PCM data" ); } else if ( config == IVAS_AUDIO_CONFIG_EXTERNAL ) { diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 02d59ec600..d51f1ff1eb 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -179,8 +179,7 @@ static int16_t parseOpts( Option *opts, const int32_t numOpts, void *pOutputStruct, - CmdLnParser_FnPtr_ParseOption parseOption -) + CmdLnParser_FnPtr_ParseOption parseOption ) { Option *currOpt = NULL; int32_t currOptIdx = 1; @@ -239,7 +238,7 @@ static int16_t parseOpts( { if ( currOpt != NULL ) { - if ( parseOption(currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct) != 0 ) + if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) { return -1; } @@ -257,7 +256,7 @@ static int16_t parseOpts( /* Parse last option */ if ( currOpt != NULL ) { - if (parseOption(currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct) != 0) + if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) { return -1; } -- GitLab From 2aeac261a6cfb9764f5b7b65dad5e614dd010650 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 3 Dec 2025 10:58:18 +0100 Subject: [PATCH 06/34] clang format --- apps/isar_post_rend.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 877220cdba..c34cbbf0c1 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -968,7 +968,7 @@ int main( if ( !isEmptyString( args.splitRendBFIFilePath ) ) { convert_backslash( args.splitRendBFIFilePath ); - if ( SplitRendBFIFileReader_open(args.splitRendBFIFilePath, &splitRendBFIReader ) != IVAS_ERR_OK ) + if ( SplitRendBFIFileReader_open( args.splitRendBFIFilePath, &splitRendBFIReader ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError opening file: %s\n", args.splitRendBFIFilePath ); goto cleanup; @@ -994,7 +994,7 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - if (isEmptyString(args.inMetadataFilePaths[0])) + if ( isEmptyString( args.inMetadataFilePaths[0] ) ) { const CmdLnParser_Option *listOption = findOptionById( CmdLnOptionId_inputMetadata ); fprintf( stderr, "\nError: Split rendering metadata file not specified, use option -%s/--%s.\n", listOption->matchShort, listOption->match ); -- GitLab From a95617dcc2ecad8830db10b7e692b9b425df19c3 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Mon, 8 Dec 2025 10:54:08 +0100 Subject: [PATCH 07/34] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: sagnowski --- apps/isar_post_rend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index c34cbbf0c1..8bfb9e99ca 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -143,7 +143,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFile, .match = "input_file", .matchShort = "i", - .description = "Path to the input file (WAV, raw PCM or scene description file)", + .description = "Path to the input file (WAV or raw PCM with BINAURAL_SPLIT_PCM input format, coded ISAR file with BINAURAL_SPLIT_CODED)", }, { .id = CmdLnOptionId_inputFormat, -- GitLab From af7fd1b6c3d0734db22b87b67df35856a2d9595e Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Mon, 8 Dec 2025 12:19:01 +0100 Subject: [PATCH 08/34] simplify --help and --l arguments --- apps/isar_post_rend.c | 48 ++++++++----------------------------------- apps/renderer.c | 5 +---- 2 files changed, 10 insertions(+), 43 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index c34cbbf0c1..5f3a454645 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -113,10 +113,7 @@ typedef struct ISAR_POST_REND_COMPLEXITY_LEVEL complexityLevel; bool delayCompensationEnabled; bool quietModeEnabled; - bool sceneDescriptionInput; IVAS_RENDER_FRAMESIZE render_framesize; - int16_t numMandatoryCmdLineParams; - bool help; } CmdlnArgs; typedef enum @@ -149,7 +146,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFormat, .match = "input_format", .matchShort = "if", - .description = "Audio format of input file (e.g. BINAURAL_SPLIT_PCM, use -l for a list)", + .description = "Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list)", }, { .id = CmdLnOptionId_outputFile, @@ -161,7 +158,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputMetadata, .match = "input_metadata", .matchShort = "im", - .description = "Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode", + .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input mode", }, { .id = CmdLnOptionId_sampleRate, @@ -301,26 +298,11 @@ static const CmdLnParser_Option *findOptionById( static bool parseInConfig( const char *inFormatStr, - InputConfig *inConfig, - bool *sceneDescriptionInput ) + InputConfig *inConfig ) { - char charBuf[FILENAME_MAX]; - /* Initialize input config struct */ inConfig->numBinBuses = 0; - /* First check if input is being set to scene description file - this is not covered by parseAudioConfig(). */ - strncpy( charBuf, inFormatStr, sizeof( charBuf ) - 1 ); - charBuf[sizeof( charBuf ) - 1] = '\0'; - to_upper( charBuf ); - if ( strcmp( charBuf, "META" ) == 0 ) - { - *sceneDescriptionInput = true; - /* Parsing the file will be done later. At this point the actual file path - * may not be known as command line parameters are still being parsed. */ - return true; - } - /* Check for single-format inputs. The given string should map to a member of AUDIO_CONFIG enum. */ bool srRtp = false; IVAS_AUDIO_CONFIG audioConfig = parseAudioConfig( inFormatStr, &srRtp ); @@ -427,11 +409,8 @@ static bool checkRequiredArgs( missingRequiredArg = true; } - const bool singleInputSpecified = ( args.inConfig.numBinBuses != 0 ); - - if ( !args.sceneDescriptionInput && !singleInputSpecified ) + if ( args.inConfig.numBinBuses == 0 ) { - /* Neither scene description input nor single-type input was specified on command line */ tmpOption = findOptionById( CmdLnOptionId_inputFormat ); fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); missingRequiredArg = true; @@ -446,7 +425,7 @@ static bool checkRequiredArgs( if ( missingRequiredArg ) { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, args.numMandatoryCmdLineParams ); + CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ); } return !missingRequiredArg; @@ -485,11 +464,8 @@ static CmdlnArgs defaultArgs( args.complexityLevel = ISAR_POST_REND_COMPLEXITY_LEVEL_THREE; args.delayCompensationEnabled = true; args.quietModeEnabled = false; - args.sceneDescriptionInput = false; args.render_framesize = IVAS_RENDER_FRAMESIZE_20MS; - args.numMandatoryCmdLineParams = POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS; - args.help = false; return args; } @@ -522,7 +498,7 @@ static int16_t parseOption( fprintf( stderr, "Error: No input file format has been provided!\n" ); return -1; } - if ( !parseInConfig( optionValues[0], &args->inConfig, &args->sceneDescriptionInput ) ) + if ( !parseInConfig( optionValues[0], &args->inConfig ) ) { /* Error printout handled by failing function */ return -1; @@ -631,8 +607,8 @@ static int16_t parseOption( strncpy( args->srParamsFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_help: - args->help = true; - break; + CmdLnParser_printUsage( args->executableName, cliOptions, numCliOptions, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ); + exit( 0 ); default: fprintf( stderr, "Error: Incorrect or invalid command-line usage!\n" ); return -1; @@ -649,17 +625,11 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, args.numMandatoryCmdLineParams ) != 0 ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } - if ( args.help ) - { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, args.numMandatoryCmdLineParams ); - exit( 0 ); - } - if ( !checkRequiredArgs( args ) ) { exit( -1 ); /* Error printout handled by failing function */ diff --git a/apps/renderer.c b/apps/renderer.c index 49b6dca223..bba0c4c231 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -196,7 +196,6 @@ typedef struct uint16_t directivityPatternId[RENDERER_MAX_ISM_INPUTS]; AcousticEnvironmentSequence aeSequence; IVAS_ROOM_SIZE_T reverbRoomSize; - int16_t numMandatoryCmdLineParams; } CmdlnArgs; typedef enum @@ -2720,8 +2719,6 @@ static CmdlnArgs defaultArgs( args.reverbRoomSize = IVAS_ROOM_SIZE_AUTO; - args.numMandatoryCmdLineParams = RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS; - return args; } @@ -2924,7 +2921,7 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, args.numMandatoryCmdLineParams ) != 0 ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } -- GitLab From 36cf1dc25a359304a3f169a260a9db8afd7b7da8 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Mon, 8 Dec 2025 12:22:48 +0100 Subject: [PATCH 09/34] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: sagnowski --- apps/isar_post_rend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 322b4ea560..2fd1fdd8db 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -1053,7 +1053,7 @@ int main( case IVAS_ERR_SAMPLING_RATE_UNKNOWN: /* Returned when input is raw PCM */ if ( args.sampleRate == 0 ) { - fprintf( stderr, "\nError: Sampling rate must be specified on command line in case of BINAURAL_SPLIT_PCM input!\n" ); + fprintf( stderr, "\nError: Sampling rate must be specified on command line in case of raw PCM input!\n" ); goto cleanup; } break; -- GitLab From 9ffbdcc769da4343c635b5f2beb81860f3f5c1c0 Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Mon, 8 Dec 2025 12:30:06 +0100 Subject: [PATCH 10/34] remove "raw" --- lib_rend/ivas_output_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_rend/ivas_output_init.c b/lib_rend/ivas_output_init.c index 0cafba8aef..2faeca21d2 100644 --- a/lib_rend/ivas_output_init.c +++ b/lib_rend/ivas_output_init.c @@ -552,11 +552,11 @@ ivas_error get_channel_config( } else if ( config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) { - strcpy( str, "Binaural: split-rendering mode using coded data" ); + strcpy( str, "Binaural: split-rendering mode using encoded data" ); } else if ( config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { - strcpy( str, "Binaural: split-rendering mode using raw PCM data" ); + strcpy( str, "Binaural: split-rendering mode using PCM data" ); } else if ( config == IVAS_AUDIO_CONFIG_EXTERNAL ) { -- GitLab From b10c0a891cddc9af326b94bf20cabc0e04cb23aa Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Mon, 8 Dec 2025 13:08:40 +0100 Subject: [PATCH 11/34] simplification of inMetadataFile string --- apps/isar_post_rend.c | 44 ++++++++++++++++++++--------------- lib_isar/lib_isar_post_rend.h | 1 - 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 2fd1fdd8db..9f13f1286f 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -106,8 +106,7 @@ typedef struct int32_t sampleRate; InputConfig inConfig; OutputConfig outConfig; - char inMetadataFilePaths[RENDERER_MAX_ISAR_MD_INPUTS][POST_REND_MAX_CLI_ARG_LENGTH]; - int16_t numInMetadataFiles; + char inMetadataFilePath[POST_REND_MAX_CLI_ARG_LENGTH]; char headRotationFilePath[POST_REND_MAX_CLI_ARG_LENGTH]; char splitRendBFIFilePath[POST_REND_MAX_CLI_ARG_LENGTH]; ISAR_POST_REND_COMPLEXITY_LEVEL complexityLevel; @@ -451,12 +450,7 @@ static CmdlnArgs defaultArgs( args.inConfig.binBuses[0].gain_dB = 0; args.outConfig.audioConfig = IVAS_AUDIO_CONFIG_INVALID; - - for ( int32_t i = 0; i < RENDERER_MAX_ISAR_MD_INPUTS; ++i ) - { - clearString( args.inMetadataFilePaths[i] ); - } - args.numInMetadataFiles = 0; + clearString( args.inMetadataFilePath ); clearString( args.headRotationFilePath ); clearString( args.splitRendBFIFilePath ); @@ -485,19 +479,30 @@ static int16_t parseOption( printSupportedAudioConfigs(); exit( 0 ); case CmdLnOptionId_inputFile: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: No input file has been provided!\n" ); return -1; } + else if (numOptionValues > 1) + { + fprintf( stderr, "Error: The program expects a single input file, but %d have been provided!\n", numOptionValues ); + return -1; + } strncpy( args->inputFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_inputFormat: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: No input file format has been provided!\n" ); return -1; } + else if ( numOptionValues > 1 ) + { + fprintf( stderr, "Error: The program expects a single input file format, but %d have been provided!\n", numOptionValues ); + return -1; + } + if ( !parseInConfig( optionValues[0], &args->inConfig ) ) { /* Error printout handled by failing function */ @@ -505,16 +510,17 @@ static int16_t parseOption( } break; case CmdLnOptionId_inputMetadata: - if ( numOptionValues < 1 || numOptionValues > RENDERER_MAX_ISAR_MD_INPUTS ) + if ( numOptionValues == 0 ) { - fprintf( stderr, "Error: No paths to metadata files for BINAURAL_SPLIT_PCM input mode has been provided!\n" ); + fprintf( stderr, "Error: No input metadata file for BINAURAL_SPLIT_PCM mode has been provided!\n" ); return -1; } - for ( int16_t i = 0; i < numOptionValues; ++i ) + else if (numOptionValues > 1) { - strncpy( args->inMetadataFilePaths[i], optionValues[i], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); + fprintf( stderr, "Error: The program expects a single input metadata file, but %d have been provided!\n", numOptionValues ); + return -1; } - args->numInMetadataFiles = numOptionValues; + strncpy( args->inMetadataFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_outputFile: if ( numOptionValues != 1 ) @@ -527,7 +533,7 @@ static int16_t parseOption( case CmdLnOptionId_sampleRate: if ( numOptionValues != 1 ) { - fprintf( stderr, "Error: No input sampling rate has been provided!\n" ); + fprintf( stderr, "Error: Incorrect input sampling rate has been provided!\n" ); return -1; } args->sampleRate = (int32_t) ( strtol( optionValues[0], NULL, 10 ) * 1000 ); @@ -955,7 +961,7 @@ int main( if ( ( args.inConfig.numBinBuses > 0 ) && ( args.inConfig.binBuses[0].audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) { error = split_rend_reader_open( &hSplitRendFileReadWrite, - args.inMetadataFilePaths[0], + args.inMetadataFilePath, &bitsBuffer.config.codec, &bitsBuffer.config.poseCorrection, &bitsBuffer.config.codec_frame_size_ms, @@ -964,14 +970,14 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - if ( isEmptyString( args.inMetadataFilePaths[0] ) ) + if ( isEmptyString( args.inMetadataFilePath ) ) { const CmdLnParser_Option *listOption = findOptionById( CmdLnOptionId_inputMetadata ); fprintf( stderr, "\nError: Split rendering metadata file not specified, use option -%s/--%s.\n", listOption->matchShort, listOption->match ); } else { - fprintf( stderr, "\nError: Could not open split rendering metadata file %s!\n", args.inMetadataFilePaths[0] ); + fprintf( stderr, "\nError: Could not open split rendering metadata file %s!\n", args.inMetadataFilePath ); } goto cleanup; } diff --git a/lib_isar/lib_isar_post_rend.h b/lib_isar/lib_isar_post_rend.h index e1f91a4769..8a2378a0a4 100644 --- a/lib_isar/lib_isar_post_rend.h +++ b/lib_isar/lib_isar_post_rend.h @@ -41,7 +41,6 @@ * ISAR post-renderer constants *---------------------------------------------------------------------*/ -#define RENDERER_MAX_ISAR_MD_INPUTS 1 #define RENDERER_MAX_BIN_INPUTS 1 /*---------------------------------------------------------------------* -- GitLab From 9eac6caeffad16415212ea54c5eddb96b330244f Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Mon, 8 Dec 2025 13:17:21 +0100 Subject: [PATCH 12/34] improve command-line parameter error messages --- apps/isar_post_rend.c | 48 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 9f13f1286f..dd99da223f 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -523,17 +523,22 @@ static int16_t parseOption( strncpy( args->inMetadataFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_outputFile: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: No output file has been provided!\n" ); return -1; } + else if ( numOptionValues > 1 ) + { + fprintf( stderr, "Error: The program expects a single output file, but %d have been provided!\n", numOptionValues ); + return -1; + } strncpy( args->outputFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_sampleRate: if ( numOptionValues != 1 ) { - fprintf( stderr, "Error: Incorrect input sampling rate has been provided!\n" ); + fprintf( stderr, "Error: Incorrect sampling rate specification!\n" ); return -1; } args->sampleRate = (int32_t) ( strtol( optionValues[0], NULL, 10 ) * 1000 ); @@ -544,27 +549,42 @@ static int16_t parseOption( } break; case CmdLnOptionId_trajFile: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: No head rotation trajectory file has been provided!\n" ); return -1; } + else if ( numOptionValues > 1 ) + { + fprintf( stderr, "Error: The program expects a single head rotation trajectory file, but %d have been provided!\n", numOptionValues ); + return -1; + } strncpy( args->headRotationFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_SplitRendBFIFile: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) + { + fprintf( stderr, "Error: No BFI file has been provided!\n" ); + return -1; + } + else if ( numOptionValues > 1 ) { - fprintf( stderr, "Error: No bfi file has been provided!\n" ); + fprintf( stderr, "Error: The program expects a single BFI file, but %d have been provided!\n", numOptionValues ); return -1; } strncpy( args->splitRendBFIFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_complexityLevel: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: No complexity level has been provided!\n" ); return -1; } + else if ( numOptionValues > 1 ) + { + fprintf( stderr, "Error: The program expects a single complexity level, but %d have been provided!\n", numOptionValues ); + return -1; + } args->complexityLevel = (int32_t) ( strtol( optionValues[0], NULL, 10 ) ); if ( args->complexityLevel < ISAR_POST_REND_COMPLEXITY_LEVEL_ONE || args->complexityLevel > ISAR_POST_REND_COMPLEXITY_LEVEL_THREE ) { @@ -593,11 +613,16 @@ static int16_t parseOption( args->quietModeEnabled = true; break; case CmdLnOptionId_framing: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) { fprintf( stderr, "Error: Incorrect audio rendering frame size!\n" ); return -1; } + else if ( numOptionValues > 1 ) + { + fprintf( stderr, "Error: The program expects a rendering frame size, but %d have been provided!\n", numOptionValues ); + return -1; + } if ( !parseRenderFramesize( optionValues[0], &args->render_framesize ) ) { fprintf( stderr, "Error: Unknown or invalid option for audio rendring frame size: %s\n", optionValues[0] ); @@ -605,9 +630,14 @@ static int16_t parseOption( } break; case CmdLnOptionId_srParamsFile: - if ( numOptionValues != 1 ) + if ( numOptionValues == 0 ) + { + fprintf( stderr, "Error: No path to the parameter initialization file has been provided!\n" ); + return -1; + } + else if ( numOptionValues > 1 ) { - fprintf( stderr, "Error: No path to split rending init params file has been provided!\n" ); + fprintf( stderr, "Error: The program expects a single parameter initialization file, but %d have been provided!\n", numOptionValues ); return -1; } strncpy( args->srParamsFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); -- GitLab From 7cdaa41916e08bf4da8828192ea760ee74dd8be7 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 9 Dec 2025 17:25:53 +0100 Subject: [PATCH 13/34] update readme --- readme.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index 98d2914fde..87f84ab550 100644 --- a/readme.txt +++ b/readme.txt @@ -389,13 +389,13 @@ Usage: ISAR_post_rend [options] Mandatory parameters: --------------------- --i File : Input File (input file is bitstream if format is BINAURAL_SPLIT_CODED, or PCM/WAV file if format is BINAURAL_SPLIT_PCM) --if Format : Input Format of input (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM) +-i File : Input Audio File (input file is bitstream if format is BINAURAL_SPLIT_CODED, or PCM/WAV file if format is BINAURAL_SPLIT_PCM) +-if Format : Format of Input File (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ...); Use -l to get a list of supported formats -o File : Output Audio File in BINAURAL format Options: -------- --im Files : Space-separated list of paths to metadata files for BINAURAL_SPLIT_PCM input mode +-im Files : Input Metadata File for BINAURAL_SPLIT_PCM format -fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs -T File : Head rotation trajectory file for simulation of head tracking -prbfi File : Split rendering BFI (Bad Frame Indicator) file -- GitLab From d2fe05293b267967ccd22055f48f983ff73e603b Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Thu, 11 Dec 2025 14:27:46 +0100 Subject: [PATCH 14/34] Integrate mandatory parameters into CmdlnParser, improve usage printout --- apps/isar_post_rend.c | 50 +++------------------ apps/renderer.c | 65 ++++----------------------- lib_util/cmdln_parser.c | 97 +++++++++++++++++++++++++++++------------ lib_util/cmdln_parser.h | 5 ++- 4 files changed, 85 insertions(+), 132 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index dd99da223f..309227dc9a 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -57,8 +57,7 @@ * Local constants *------------------------------------------------------------------------------------------*/ -#define POST_REND_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) -#define POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS 3 +#define POST_REND_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) #define ISAR_MAX16B_FLT 32767.0f #define ISAR_MIN16B_FLT ( -32768.0f ) @@ -140,18 +139,21 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_file", .matchShort = "i", .description = "Path to the input file (WAV or raw PCM with BINAURAL_SPLIT_PCM input format, coded ISAR file with BINAURAL_SPLIT_CODED)", + .isMandatory = true, }, { .id = CmdLnOptionId_inputFormat, .match = "input_format", .matchShort = "if", .description = "Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list)", + .isMandatory = true, }, { .id = CmdLnOptionId_outputFile, .match = "output_file", .matchShort = "o", .description = "Path to the output file", + .isMandatory = true, }, { .id = CmdLnOptionId_inputMetadata, @@ -394,41 +396,6 @@ static IVAS_AUDIO_CONFIG parseAudioConfig( } -static bool checkRequiredArgs( - CmdlnArgs args ) -{ - const CmdLnParser_Option *tmpOption; - - /* Check required arguments */ - bool missingRequiredArg = false; - if ( isEmptyString( args.inputFilePath ) ) - { - tmpOption = findOptionById( CmdLnOptionId_inputFile ); - fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - - if ( args.inConfig.numBinBuses == 0 ) - { - tmpOption = findOptionById( CmdLnOptionId_inputFormat ); - fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - - if ( isEmptyString( args.outputFilePath ) ) - { - tmpOption = findOptionById( CmdLnOptionId_outputFile ); - fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - - if ( missingRequiredArg ) - { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ); - } - - return !missingRequiredArg; -} static CmdlnArgs defaultArgs( @@ -643,7 +610,7 @@ static int16_t parseOption( strncpy( args->srParamsFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; case CmdLnOptionId_help: - CmdLnParser_printUsage( args->executableName, cliOptions, numCliOptions, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ); + CmdLnParser_printUsage( args->executableName, cliOptions, numCliOptions ); exit( 0 ); default: fprintf( stderr, "Error: Incorrect or invalid command-line usage!\n" ); @@ -661,12 +628,7 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, POST_REND_NUM_MANDATORY_CMD_LINE_PARAMS ) != 0 ) - { - exit( -1 ); /* Error printout handled by failing function */ - } - - if ( !checkRequiredArgs( args ) ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } diff --git a/apps/renderer.c b/apps/renderer.c index bba0c4c231..83315bc4cf 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -65,10 +65,9 @@ * Local constants *------------------------------------------------------------------------------------------*/ -#define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) -#define RENDERER_MAX_METADATA_LENGTH 8192 -#define RENDERER_MAX_METADATA_LINE_LENGTH 1024 -#define RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS 1000 +#define RENDERER_MAX_CLI_ARG_LENGTH ( FILENAME_MAX ) +#define RENDERER_MAX_METADATA_LENGTH 8192 +#define RENDERER_MAX_METADATA_LINE_LENGTH 1024 #define IVAS_MAX16B_FLT 32767.0f #define IVAS_MIN16B_FLT ( -32768.0f ) @@ -236,12 +235,14 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_file", .matchShort = "i", .description = "Path to the input file (WAV, raw PCM or scene description file)", + .isMandatory = true, }, { .id = CmdLnOptionId_inputFormat, .match = "input_format", .matchShort = "if", .description = "Audio format of input file (e.g. 5_1 or HOA3 or META,\nuse -l for a list)", + .isMandatory = true, }, { .id = CmdLnOptionId_inputMetadata, @@ -254,12 +255,14 @@ static const CmdLnParser_Option cliOptions[] = { .match = "output_file", .matchShort = "o", .description = "Path to the output file", + .isMandatory = true, }, { .id = CmdLnOptionId_outputFormat, .match = "output_format", .matchShort = "of", .description = "Output format to render.\nAlternatively, can be a custom loudspeaker layout file", + .isMandatory = true, }, { .id = CmdLnOptionId_sampleRate, @@ -2596,53 +2599,6 @@ static bool parseReverbRoomSize( return true; } - -static bool checkRequiredArgs( - CmdlnArgs args ) -{ - const CmdLnParser_Option *tmpOption; - - /* Check required arguments */ - bool missingRequiredArg = false; - if ( isEmptyString( args.inputFilePath ) ) - { - tmpOption = findOptionById( CmdLnOptionId_inputFile ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - - const bool singleInputSpecified = args.inConfig.numAudioObjects != 0 || - args.inConfig.numAmbisonicsBuses != 0 || - args.inConfig.numMultiChannelBuses != 0 || - args.inConfig.numMasaBuses != 0; - - if ( !args.sceneDescriptionInput && !singleInputSpecified ) - { - /* Neither scene description input nor single-type input was specified on command line */ - tmpOption = findOptionById( CmdLnOptionId_inputFormat ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - if ( isEmptyString( args.outputFilePath ) ) - { - tmpOption = findOptionById( CmdLnOptionId_outputFile ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_INVALID ) - { - tmpOption = findOptionById( CmdLnOptionId_outputFormat ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - if ( missingRequiredArg ) - { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions, 100 ); - } - - return !missingRequiredArg; -} - static CmdlnArgs defaultArgs( const char *executableName ) { @@ -2921,12 +2877,7 @@ static CmdlnArgs parseCmdlnArgs( { CmdlnArgs args = defaultArgs( argv[0] ); - if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption, RENDERER_NUM_MANDATORY_CMD_LINE_PARAMS ) != 0 ) - { - exit( -1 ); /* Error printout handled by failing function */ - } - - if ( !checkRequiredArgs( args ) ) + if ( CmdLnParser_parseArgs( argc, argv, cliOptions, numCliOptions, &args, parseOption ) != 0 ) { exit( -1 ); /* Error printout handled by failing function */ } diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index d51f1ff1eb..be7abc62ce 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -131,7 +131,7 @@ static int8_t stringLooksLikeOption( static const char *stringToOptionName( const char *str ) { - while ( ( *str == '-' ) && ( ( str[1] != '0' ) || ( str[1] != '1' ) ) ) + while ( *str == '-' ) { ++str; } @@ -202,7 +202,7 @@ static int16_t parseOpts( /* Check if already parsed */ if ( optToMatch->hasBeenParsed ) { - fprintf( stderr, "Duplicate option: %s (%s)\n", optToMatch->props.match, optToMatch->props.matchShort ); + fprintf( stderr, "Duplicate option provided: --%s/-%s\n", optToMatch->props.match, optToMatch->props.matchShort ); return -1; } @@ -216,7 +216,7 @@ static int16_t parseOpts( /* Invalid option */ if ( stringLooksLikeOption( argv[argIdx] ) ) { - fprintf( stderr, "Unknown option `%s`\n", stringToOptionName( argv[argIdx] ) ); + fprintf( stderr, "Unknown option `%s`\n", argv[argIdx] ); return -1; } @@ -263,6 +263,23 @@ static int16_t parseOpts( currOpt->hasBeenParsed = 1; } + /* Check mandatory options */ + bool missingMandatory = false; + for ( int32_t optIdx = 0; optIdx < numOpts; ++optIdx ) + { + Option opt = opts[optIdx]; + + if ( opt.props.isMandatory && !opt.hasBeenParsed ) + { + fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", opt.props.match, opt.props.matchShort ); + missingMandatory = true; + } + } + if ( missingMandatory ) + { + return -1; + } + return 0; } @@ -331,50 +348,74 @@ static void printOptDescriptionAligned( return; } +static void printOptions( + const OptionProps *optionProps, + const int32_t numOptions, + const bool mandatory, + const int32_t maxOptNameLength + ) +{ + const int32_t preDescriptionWhitespace = 4; + const int32_t leftColumnAdditionalChars = 7; + int32_t optNameLength; + + for ( int32_t i = 0; i < numOptions; ++i ) + { + OptionProps opt = optionProps[i]; + + if ( opt.isMandatory != mandatory ) + { + continue; + } + + optNameLength = totalOptionNameLength( optionProps[i] ); + + fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); + + printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace - 2 ); + fprintf( stderr, ": " ); + printOptDescriptionAligned( opt.description, maxOptNameLength + leftColumnAdditionalChars + preDescriptionWhitespace ); + } +} + static void printUsage( const char *argv0, const OptionProps *optionProps, - const int32_t numOptions, - const int16_t numMandatoryCmdLineParams ) + const int32_t numOptions ) { int32_t optNameLength; + bool hasMandatoryOptions = false; fprintf( stderr, "\n" ); fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) ); fprintf( stderr, "\n" ); - /* Find option with longest name, used for pretty formatting */ int32_t maxOptNameLength = 0; for ( int32_t i = 0; i < numOptions; ++i ) { + /* Find option with longest name, used for pretty formatting */ optNameLength = totalOptionNameLength( optionProps[i] ); if ( maxOptNameLength < optNameLength ) { maxOptNameLength = optNameLength; } - } - - fprintf( stderr, "Mandatory parameters:\n" ); - - const int32_t preDescriptionWhitespace = 8; - const int32_t leftColumnAdditionalChars = 7; - for ( int32_t i = 0; i < numOptions; ++i ) - { - OptionProps opt = optionProps[i]; - optNameLength = totalOptionNameLength( optionProps[i] ); - if ( i == numMandatoryCmdLineParams ) + /* Check if mandatory parameters should be printed separately */ + if ( optionProps[i].isMandatory ) { - fprintf( stderr, "\nOptions:\n" ); + hasMandatoryOptions = true; } + } - /* TODO(sgi): make matchShort optional */ - fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); - - printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace ); - printOptDescriptionAligned( opt.description, maxOptNameLength + preDescriptionWhitespace + leftColumnAdditionalChars ); + if ( hasMandatoryOptions ) + { + fprintf( stderr, "Mandatory parameters:\n---------------------\n" ); + printOptions( optionProps, numOptions, true, maxOptNameLength ); } + fprintf( stderr, "\nOptions:\n--------\n" ); + printOptions( optionProps, numOptions, false, maxOptNameLength ); + return; } @@ -384,8 +425,7 @@ int16_t CmdLnParser_parseArgs( const OptionProps *optionProps, const int32_t numOptions, void *pOutputStruct, - CmdLnParser_FnPtr_ParseOption parseOption, - const int16_t numMandatoryCmdLineParams ) + CmdLnParser_FnPtr_ParseOption parseOption ) { assert( numOptions <= MAX_SUPPORTED_OPTS ); @@ -405,17 +445,16 @@ int16_t CmdLnParser_parseArgs( return 0; fail: - printUsage( argv[0], optionProps, numOptions, numMandatoryCmdLineParams ); + printUsage( argv[0], optionProps, numOptions ); return -1; } void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, - const int32_t numOptions, - const int16_t numMandatoryCmdLineParams ) + const int32_t numOptions ) { - printUsage( executableName, options, numOptions, numMandatoryCmdLineParams ); + printUsage( executableName, options, numOptions ); return; } diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index 58deb4a41e..03289f4d29 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -42,13 +42,14 @@ typedef struct const char *match; const char *matchShort; const char *description; + bool isMandatory; } CmdLnParser_Option; /* Function for parsing option values into an output struct, to be implemented by the user */ typedef int16_t ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, char **optionValues, int16_t numOptionValues, void *pOutputStruct ); -int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption, const int16_t numMandatoryCmdLineParams ); +int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption ); -void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions, const int16_t numMandatoryCmdLineParams ); +void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions ); #endif /* CMDLN_PARSER_H */ -- GitLab From 0b29b18883a25b5eea448bfb36b9364c8f64bc3f Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Thu, 11 Dec 2025 16:05:56 +0100 Subject: [PATCH 15/34] CmdLnParser: add placeholders for option values in usage printout --- apps/isar_post_rend.c | 12 ++++++++- apps/renderer.c | 29 ++++++++++++++++++-- lib_util/cmdln_parser.c | 60 ++++++++++++++++++++++++++--------------- lib_util/cmdln_parser.h | 11 ++++---- 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 309227dc9a..c67b21425c 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -138,6 +138,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFile, .match = "input_file", .matchShort = "i", + .placeholder = "", .description = "Path to the input file (WAV or raw PCM with BINAURAL_SPLIT_PCM input format, coded ISAR file with BINAURAL_SPLIT_CODED)", .isMandatory = true, }, @@ -145,6 +146,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFormat, .match = "input_format", .matchShort = "if", + .placeholder = "", .description = "Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list)", .isMandatory = true, }, @@ -152,6 +154,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_outputFile, .match = "output_file", .matchShort = "o", + .placeholder = "", .description = "Path to the output file", .isMandatory = true, }, @@ -159,24 +162,28 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputMetadata, .match = "input_metadata", .matchShort = "im", + .placeholder = "", .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input mode", }, { .id = CmdLnOptionId_sampleRate, .match = "sample_rate", .matchShort = "fs", + .placeholder = "", .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs", }, { .id = CmdLnOptionId_trajFile, .match = "trajectory_file", .matchShort = "T", + .placeholder = "", .description = "Head rotation trajectory file for simulation of head tracking", }, { .id = CmdLnOptionId_SplitRendBFIFile, .match = "post_rend_bfi_file", .matchShort = "prbfi", + .placeholder = "", .description = "Split rendering BFI (Bad Frame Indicator) file", }, { @@ -189,6 +196,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_complexityLevel, .match = "complexity_level", .matchShort = "level", + .placeholder = "", .description = "Complexity level, level = (1, 2, 3), will be defined after characterisation.", }, { @@ -207,12 +215,14 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_framing, .match = "framing", .matchShort = "fr", - .description = "Set audio rendering frame size", + .placeholder = "", + .description = "Set audio rendering frame size in ms (5, 10, 20)", }, { .id = CmdLnOptionId_srParamsFile, .match = "sr_params", .matchShort = "s", + .placeholder = "", .description = "Path to the split rendering init params file", }, { diff --git a/apps/renderer.c b/apps/renderer.c index 83315bc4cf..3169371560 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -234,6 +234,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFile, .match = "input_file", .matchShort = "i", + .placeholder = "", .description = "Path to the input file (WAV, raw PCM or scene description file)", .isMandatory = true, }, @@ -241,6 +242,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputFormat, .match = "input_format", .matchShort = "if", + .placeholder = "", .description = "Audio format of input file (e.g. 5_1 or HOA3 or META,\nuse -l for a list)", .isMandatory = true, }, @@ -248,12 +250,14 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputMetadata, .match = "input_metadata", .matchShort = "im", + .placeholder = " [...]", .description = "Space-separated list of path to metadata files for ISM/MASA/OMASA/\nOSBA/BINAURAL_SPLIT_PCM inputs. \nFor OMASA, ISM files must be specified first.", }, { .id = CmdLnOptionId_outputFile, .match = "output_file", .matchShort = "o", + .placeholder = "", .description = "Path to the output file", .isMandatory = true, }, @@ -261,6 +265,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_outputFormat, .match = "output_format", .matchShort = "of", + .placeholder = "", .description = "Output format to render.\nAlternatively, can be a custom loudspeaker layout file", .isMandatory = true, }, @@ -268,66 +273,77 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_sampleRate, .match = "sample_rate", .matchShort = "fs", + .placeholder = "", .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw\nPCM inputs", }, { .id = CmdLnOptionId_trajFile, .match = "trajectory_file", .matchShort = "T", + .placeholder = "", .description = "Head rotation trajectory file for simulation of head tracking\n(only for binaural outputs)", }, { .id = CmdLnOptionId_outputMetadata, .match = "output_metadata", .matchShort = "om", + .placeholder = "", .description = "coded metadata file for BINAURAL_SPLIT_PCM output mode", }, { .id = CmdLnOptionId_SplitRendBFIFile, .match = "post_rend_bfi_file", .matchShort = "prbfi", + .placeholder = "", .description = "Split rendering option: bfi file", }, { .id = CmdLnOptionId_refRotFile, .match = "reference_rotation_file", .matchShort = "rf", + .placeholder = "", .description = "Reference rotation trajectory file for simulation of head tracking\n(only for binaural outputs)", }, { .id = CmdLnOptionId_customHrtfFile, .match = "custom_hrtf", .matchShort = "hrtf", + .placeholder = "", .description = "Custom HRTF file for binaural rendering\n(only for binaural outputs)", }, { .id = CmdLnOptionId_renderConfigFile, .match = "render_config_parameters", .matchShort = "render_config", + .placeholder = "", .description = "Binaural renderer configuration parameters in file\n(only for binaural outputs)", }, { .id = CmdLnOptionId_nonDiegeticPan, .match = "non_diegetic_panning", .matchShort = "non_diegetic_pan", + .placeholder = "", .description = "Panning mono non diegetic sound to stereo -90<= pan <= 90\nleft or l or 90->left, right or r or -90->right,\ncenter or c or 0 ->middle", }, { .id = CmdLnOptionId_orientationTracking, .match = "tracking_type", .matchShort = "otr", + .placeholder = "", .description = "Head orientation tracking type: 'none', 'ref', 'avg' or `ref_vec`\nor `ref_vec_lev` (only for binaural outputs)", }, { .id = CmdlnOptionId_lfePosition, .match = "lfe_position", .matchShort = "lp", + .placeholder = ",,", .description = "Output LFE position. Comma-delimited triplet of [gain, azimuth,\nelevation] where gain is linear (like --gain, -g) and azimuth,\nelevation are in degrees. If specified, overrides the default\nbehavior which attempts to map input to output LFE channel(s)", }, { .id = CmdlnOptionId_lfeMatrix, .match = "lfe_matrix", .matchShort = "lm", + .placeholder = "", .description = "LFE panning matrix. File (CSV table) containing a matrix of\ndimensions [ num_input_lfe x num_output_channels ] with elements\nspecifying linear routing gain (like --gain, -g). If specified,\noverrides the output LFE position option and the default\nbehavior which attempts to map input to output LFE channel(s)", }, { @@ -340,6 +356,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_complexityLevel, .match = "complexity_level", .matchShort = "level", + .placeholder = "", .description = "Complexity level, level = (1, 2, 3), will be defined after\ncharacterisation.", }, { @@ -352,6 +369,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_inputGain, .match = "gain", .matchShort = "g", + .placeholder = "", .description = "Input gain (linear, not in dB) to be applied to input audio file", }, { @@ -364,43 +382,50 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_referenceVectorFile, .match = "reference_vector_file", .matchShort = "rvf", + .placeholder = "", .description = "Reference vector trajectory file for simulation of head tracking\n(only for binaural outputs)", }, { .id = CmdLnOptionId_exteriorOrientationFile, .match = "exterior_orientation_file", .matchShort = "exof", + .placeholder = "", .description = "External orientation trajectory file for simulation of external\norientations", }, { .id = CmdLnOptionId_framing, .match = "framing", .matchShort = "fr", - .description = "Set Render audio framing.", + .placeholder = "", + .description = "Set render audio framing in ms", }, { .id = CmdLnOptionId_syncMdDelay, .match = "sync_md_delay", .matchShort = "smd", + .placeholder = "", .description = "Metadata Synchronization Delay in ms, Default is 0. Quantized by\n5ms subframes for TDRenderer (13ms -> 10ms -> 2subframes)", }, { .id = CmdLnOptionId_directivityPatternId, .match = "ism_directivity_pattern_id", .matchShort = "dpid", + .placeholder = " [...]", .description = "Directivity pattern ID(s) = [ID1, ID2, ID3, ID4]. Space-separated\nlist of up to 4 numbers (unsigned integers) can be specified for\nBINAURAL and BINAURAL_ROOM_REVERB output.\nID1, ID2, ID3, ID4 specify the directivity pattern IDs used for\nISMs 1,2,3 and 4 respectively. \nThis option needs to be accompanied by a render_config file,\notherwise a default directivity pattern is used.", }, { .id = CmdLnOptionId_acousticEnvironmentId, .match = "acoustic_environment_id", .matchShort = "aeid", + .placeholder = "", .description = "Acoustic environment ID (number > 0) alternatively, it can be\na text file where each line contains \"ID duration\" for\nBINAURAL_ROOM_REVERB output.", }, { .id = CmdLnOptionId_roomSize, .match = "room_size", .matchShort = "rsz", - .description = "Selects default reverb based on a room size (S - small | M - medium | L - large)", + .placeholder = "", + .description = "Selects default reverb based on a room size (S - small |\nM - medium | L - large)", } }; diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index be7abc62ce..c6a593b1f6 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -304,15 +304,24 @@ static const char *getBasename( return path; } -static int32_t totalOptionNameLength( +static int32_t totalNumOptChars( const OptionProps opt ) { - return (int32_t) ( strlen( opt.match ) + strlen( opt.matchShort ) ); + int32_t len = (int32_t) strlen( opt.match ) + strlen( opt.matchShort ); + + if ( opt.placeholder != NULL ) + { + len += (int32_t) strlen( opt.placeholder ); + } + + return len; } static void printWhitespace( const int32_t n ) { + assert( n >= 0 ); + for ( int32_t i = 0; i < n; ++i ) { fprintf( stderr, " " ); @@ -352,12 +361,11 @@ static void printOptions( const OptionProps *optionProps, const int32_t numOptions, const bool mandatory, - const int32_t maxOptNameLength + const int32_t maxNumOptChars ) { - const int32_t preDescriptionWhitespace = 4; - const int32_t leftColumnAdditionalChars = 7; - int32_t optNameLength; + const int32_t descriptionColumnIdx = maxNumOptChars + 11 /* Additional chars we will print in the options column */; + int32_t numOptChars; for ( int32_t i = 0; i < numOptions; ++i ) { @@ -368,13 +376,23 @@ static void printOptions( continue; } - optNameLength = totalOptionNameLength( optionProps[i] ); + numOptChars = totalNumOptChars( optionProps[i] ); - fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); + if ( opt.placeholder != NULL ) + { + fprintf( stderr, " --%s, -%s %s", opt.match, opt.matchShort, opt.placeholder ); + numOptChars += 8; + } + else + { + fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); + numOptChars += 7; + } - printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace - 2 ); + /* Done printing options column, fill with whitespace until description column */ + printWhitespace( descriptionColumnIdx - numOptChars - 2 /* account for ": " below */ ); fprintf( stderr, ": " ); - printOptDescriptionAligned( opt.description, maxOptNameLength + leftColumnAdditionalChars + preDescriptionWhitespace ); + printOptDescriptionAligned( opt.description, descriptionColumnIdx ); } } @@ -383,21 +401,20 @@ static void printUsage( const OptionProps *optionProps, const int32_t numOptions ) { - int32_t optNameLength; - bool hasMandatoryOptions = false; - fprintf( stderr, "\n" ); fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) ); fprintf( stderr, "\n" ); - int32_t maxOptNameLength = 0; + int32_t numOptChars; + int32_t maxNumOptChars = 0; + bool hasMandatoryOptions = false; for ( int32_t i = 0; i < numOptions; ++i ) { - /* Find option with longest name, used for pretty formatting */ - optNameLength = totalOptionNameLength( optionProps[i] ); - if ( maxOptNameLength < optNameLength ) + /* Find option with most characters when printed, used for pretty formatting */ + numOptChars = totalNumOptChars( optionProps[i] ); + if ( maxNumOptChars < numOptChars ) { - maxOptNameLength = optNameLength; + maxNumOptChars = numOptChars; } /* Check if mandatory parameters should be printed separately */ @@ -410,11 +427,12 @@ static void printUsage( if ( hasMandatoryOptions ) { fprintf( stderr, "Mandatory parameters:\n---------------------\n" ); - printOptions( optionProps, numOptions, true, maxOptNameLength ); + printOptions( optionProps, numOptions, true, maxNumOptChars ); + fprintf( stderr, "\n" ); } - fprintf( stderr, "\nOptions:\n--------\n" ); - printOptions( optionProps, numOptions, false, maxOptNameLength ); + fprintf( stderr, "Options:\n--------\n" ); + printOptions( optionProps, numOptions, false, maxNumOptChars ); return; } diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index 03289f4d29..36d216859d 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -38,11 +38,12 @@ typedef struct { - int32_t id; - const char *match; - const char *matchShort; - const char *description; - bool isMandatory; + int32_t id; /* Unique ID for the option */ + const char *match; /* String to match, e.g. "input" here will match "--input" on CLI */ + const char *matchShort; /* Short version of the string to match, e.g. "i" here will match "-i" on CLI */ + const char *placeholder; /* If not NULL, this will follow the match string in the usage printout, e.g. "" here will print "--input " on CLI */ + const char *description; /* Description of the option for the usage printout. May contain '\n' for line breaks. */ + bool isMandatory; /* Parsing will fail if an option with `isMandatory == true` is not given */ } CmdLnParser_Option; /* Function for parsing option values into an output struct, to be implemented by the user */ -- GitLab From 8009cc260a541ac4a2224c4e6ad39ea769de3708 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Thu, 11 Dec 2025 16:48:39 +0100 Subject: [PATCH 16/34] CmdLnParser: Clean up handling of optional properties in CmdLnParser_Option --- lib_util/cmdln_parser.c | 62 ++++++++++++++++++++++++++++------------- lib_util/cmdln_parser.h | 6 ++-- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index c6a593b1f6..7123f46da3 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -120,7 +120,7 @@ static int16_t initOpts( static int8_t stringLooksLikeOption( const char *str ) { - if ( ( str[0] == '-' ) && is_number( str ) == false ) + if ( ( str[0] == '-' ) && !is_number( str ) ) { return 1; } @@ -150,26 +150,32 @@ static int8_t optionMatchesString( const char *optionName = stringToOptionName( str ); - char optionName_to_upper[FILENAME_MAX]; + char optionName_to_upper[MAX_OPTION_LENGTH + 1]; strncpy( optionName_to_upper, optionName, sizeof( optionName_to_upper ) - 1 ); optionName_to_upper[sizeof( optionName_to_upper ) - 1] = '\0'; to_upper( optionName_to_upper ); - char match_to_upper[FILENAME_MAX]; + char match_to_upper[MAX_OPTION_LENGTH + 1]; strncpy( match_to_upper, opt.props.match, sizeof( match_to_upper ) - 1 ); optionName_to_upper[sizeof( match_to_upper ) - 1] = '\0'; to_upper( match_to_upper ); - - char matchShort_to_upper[FILENAME_MAX]; - strncpy( matchShort_to_upper, opt.props.matchShort, sizeof( matchShort_to_upper ) - 1 ); - optionName_to_upper[sizeof( matchShort_to_upper ) - 1] = '\0'; - to_upper( matchShort_to_upper ); - - if ( strncmp( optionName_to_upper, match_to_upper, MAX_OPTION_LENGTH ) == 0 || strncmp( optionName_to_upper, matchShort_to_upper, MAX_OPTION_LENGTH ) == 0 ) + if ( strncmp( optionName_to_upper, match_to_upper, MAX_OPTION_LENGTH ) == 0 ) { return 1; } + if ( opt.props.matchShort != NULL ) + { + strncpy( match_to_upper, opt.props.matchShort, sizeof( match_to_upper ) - 1 ); + optionName_to_upper[sizeof( match_to_upper ) - 1] = '\0'; + to_upper( match_to_upper ); + + if ( strncmp( optionName_to_upper, match_to_upper, MAX_OPTION_LENGTH ) == 0 ) + { + return 1; + } + } + return 0; } @@ -307,7 +313,12 @@ static const char *getBasename( static int32_t totalNumOptChars( const OptionProps opt ) { - int32_t len = (int32_t) strlen( opt.match ) + strlen( opt.matchShort ); + int32_t len = (int32_t) strlen( opt.match ); + + if ( opt.matchShort != NULL ) + { + len += (int32_t) strlen( opt.matchShort ); + } if ( opt.placeholder != NULL ) { @@ -378,21 +389,32 @@ static void printOptions( numOptChars = totalNumOptChars( optionProps[i] ); + fprintf( stderr, " --%s", opt.match ); + numOptChars += 4; + + if ( opt.matchShort != NULL ) + { + fprintf( stderr, ", -%s", opt.matchShort ); + numOptChars += 3; + } + if ( opt.placeholder != NULL ) { - fprintf( stderr, " --%s, -%s %s", opt.match, opt.matchShort, opt.placeholder ); - numOptChars += 8; + fprintf( stderr, " %s", opt.placeholder ); + numOptChars += 1; + } + + if ( opt.description != NULL ) + { + /* Done printing options column, fill with whitespace until description column */ + printWhitespace( descriptionColumnIdx - numOptChars - 2 /* account for ": " below */ ); + fprintf( stderr, ": " ); + printOptDescriptionAligned( opt.description, descriptionColumnIdx ); } else { - fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); - numOptChars += 7; + fprintf( stderr, "\n" ); } - - /* Done printing options column, fill with whitespace until description column */ - printWhitespace( descriptionColumnIdx - numOptChars - 2 /* account for ": " below */ ); - fprintf( stderr, ": " ); - printOptDescriptionAligned( opt.description, descriptionColumnIdx ); } } diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index 36d216859d..ddb16c01d6 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -38,8 +38,10 @@ typedef struct { - int32_t id; /* Unique ID for the option */ - const char *match; /* String to match, e.g. "input" here will match "--input" on CLI */ + int32_t id; /* Unique ID for the option */ + const char *match; /* String to match, e.g. "input" here will match "--input" on CLI */ + + /* Struct members below are optional and can be omitted in option definition. If omitted, C will implicitly set them to 0. */ const char *matchShort; /* Short version of the string to match, e.g. "i" here will match "-i" on CLI */ const char *placeholder; /* If not NULL, this will follow the match string in the usage printout, e.g. "" here will print "--input " on CLI */ const char *description; /* Description of the option for the usage printout. May contain '\n' for line breaks. */ -- GitLab From b5575c2161ebc2d498e37deafa886af121297ac2 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Thu, 11 Dec 2025 17:00:12 +0100 Subject: [PATCH 17/34] CmdLnParser: Ensure match strings are within length limit --- lib_util/cmdln_parser.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 7123f46da3..73d486313b 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -38,7 +38,7 @@ #include #define MAX_SUPPORTED_OPTS ( 1024 ) -#define MAX_OPTION_LENGTH ( 1024 ) +#define MAX_OPTION_LENGTH ( 32 ) typedef CmdLnParser_Option OptionProps; @@ -74,7 +74,7 @@ static int16_t validateOptionProps( if ( props.match == NULL ) { /* TODO(sgi): Don't print out usage after this - props.match is used there */ - fprintf( stderr, "[dev] Option with ID == %d - missing required property \"match\"\n", props.id ); + fprintf( stderr, "[dev] Option with ID %d - missing required property \"match\"\n", props.id ); return -1; } @@ -84,6 +84,18 @@ static int16_t validateOptionProps( return -1; } + /* Check match string length */ + if ( strnlen( props.match, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + { + fprintf( stderr, "[dev] Option with ID %d - match string exceeds limit of %d characters.\n", props.id, MAX_OPTION_LENGTH ); + return -1; + } + if ( props.matchShort != NULL && strnlen( props.matchShort, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + { + fprintf( stderr, "[dev] Option with ID %d - matchShort string exceeds limit of %d characters.\n", props.id, MAX_OPTION_LENGTH ); + return -1; + } + return 0; } @@ -128,6 +140,7 @@ static int8_t stringLooksLikeOption( return 0; } +// TODO: too lenient. Should strip one '-' for short and two '-' for match static const char *stringToOptionName( const char *str ) { @@ -208,7 +221,13 @@ static int16_t parseOpts( /* Check if already parsed */ if ( optToMatch->hasBeenParsed ) { - fprintf( stderr, "Duplicate option provided: --%s/-%s\n", optToMatch->props.match, optToMatch->props.matchShort ); + fprintf( stderr, "Duplicate option provided: --%s", optToMatch->props.match ); + if ( optToMatch->props.matchShort != NULL ) + { + fprintf( stderr, "/-%s", optToMatch->props.matchShort ); + } + fprintf( stderr, "\n" ); + return -1; } @@ -277,7 +296,13 @@ static int16_t parseOpts( if ( opt.props.isMandatory && !opt.hasBeenParsed ) { - fprintf( stderr, "Missing mandatory parameter: --%s/-%s\n", opt.props.match, opt.props.matchShort ); + fprintf( stderr, "Missing mandatory parameter: --%s", opt.props.match ); + if ( opt.props.matchShort != NULL ) + { + fprintf( stderr, "/-%s", opt.props.matchShort ); + } + fprintf( stderr, "\n" ); + missingMandatory = true; } } -- GitLab From 003cbb537c2667cfab79688a8b3dc48d4f77e4a8 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Thu, 11 Dec 2025 18:08:17 +0100 Subject: [PATCH 18/34] CmdLnParser: Make option matching more robust --- lib_util/cmdln_parser.c | 55 +++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 73d486313b..fe7e4e5bd5 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -37,8 +37,9 @@ #include #include -#define MAX_SUPPORTED_OPTS ( 1024 ) -#define MAX_OPTION_LENGTH ( 32 ) +#define MAX_SUPPORTED_OPTS ( 1024 ) +#define MAX_OPTION_MATCH_LENGTH ( 30 ) +#define MAX_OPTION_LENGTH ( MAX_OPTION_MATCH_LENGTH + 2 ) typedef CmdLnParser_Option OptionProps; @@ -85,14 +86,14 @@ static int16_t validateOptionProps( } /* Check match string length */ - if ( strnlen( props.match, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + if ( strnlen( props.match, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { - fprintf( stderr, "[dev] Option with ID %d - match string exceeds limit of %d characters.\n", props.id, MAX_OPTION_LENGTH ); + fprintf( stderr, "[dev] Option with ID %d - match string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); return -1; } - if ( props.matchShort != NULL && strnlen( props.matchShort, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + if ( props.matchShort != NULL && strnlen( props.matchShort, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { - fprintf( stderr, "[dev] Option with ID %d - matchShort string exceeds limit of %d characters.\n", props.id, MAX_OPTION_LENGTH ); + fprintf( stderr, "[dev] Option with ID %d - matchShort string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); return -1; } @@ -140,18 +141,6 @@ static int8_t stringLooksLikeOption( return 0; } -// TODO: too lenient. Should strip one '-' for short and two '-' for match -static const char *stringToOptionName( - const char *str ) -{ - while ( *str == '-' ) - { - ++str; - } - - return str; -} - static int8_t optionMatchesString( Option opt, const char *str ) @@ -161,29 +150,30 @@ static int8_t optionMatchesString( return 0; } - const char *optionName = stringToOptionName( str ); + if ( strnlen( str, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + { + /* String longer than longest possible option - not a match */ + return 0; + } - char optionName_to_upper[MAX_OPTION_LENGTH + 1]; - strncpy( optionName_to_upper, optionName, sizeof( optionName_to_upper ) - 1 ); - optionName_to_upper[sizeof( optionName_to_upper ) - 1] = '\0'; - to_upper( optionName_to_upper ); + char str_to_upper[MAX_OPTION_LENGTH + 1]; + snprintf( str_to_upper, sizeof( str_to_upper ), "%s", str ); + to_upper( str_to_upper ); char match_to_upper[MAX_OPTION_LENGTH + 1]; - strncpy( match_to_upper, opt.props.match, sizeof( match_to_upper ) - 1 ); - optionName_to_upper[sizeof( match_to_upper ) - 1] = '\0'; + snprintf( match_to_upper, sizeof( match_to_upper ), "--%s", opt.props.match ); to_upper( match_to_upper ); - if ( strncmp( optionName_to_upper, match_to_upper, MAX_OPTION_LENGTH ) == 0 ) + if ( strcmp( str_to_upper, match_to_upper ) == 0 ) { return 1; } if ( opt.props.matchShort != NULL ) { - strncpy( match_to_upper, opt.props.matchShort, sizeof( match_to_upper ) - 1 ); - optionName_to_upper[sizeof( match_to_upper ) - 1] = '\0'; + snprintf( match_to_upper, sizeof( match_to_upper ), "-%s", opt.props.matchShort ); to_upper( match_to_upper ); - if ( strncmp( optionName_to_upper, match_to_upper, MAX_OPTION_LENGTH ) == 0 ) + if ( strcmp( str_to_upper, match_to_upper ) == 0 ) { return 1; } @@ -342,12 +332,12 @@ static int32_t totalNumOptChars( if ( opt.matchShort != NULL ) { - len += (int32_t) strlen( opt.matchShort ); + len += (int32_t) strlen( opt.matchShort ); } if ( opt.placeholder != NULL ) { - len += (int32_t) strlen( opt.placeholder ); + len += (int32_t) strlen( opt.placeholder ); } return len; @@ -397,8 +387,7 @@ static void printOptions( const OptionProps *optionProps, const int32_t numOptions, const bool mandatory, - const int32_t maxNumOptChars - ) + const int32_t maxNumOptChars ) { const int32_t descriptionColumnIdx = maxNumOptChars + 11 /* Additional chars we will print in the options column */; int32_t numOptChars; -- GitLab From 8e4664ea85ab7a90025be7a35993ca68e7cfd365 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Fri, 12 Dec 2025 13:35:07 +0100 Subject: [PATCH 19/34] CmdLnParser: Use bool where appropriate --- lib_util/cmdln_parser.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index fe7e4e5bd5..dece415b4d 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #define MAX_SUPPORTED_OPTS ( 1024 ) #define MAX_OPTION_MATCH_LENGTH ( 30 ) @@ -46,7 +48,7 @@ typedef CmdLnParser_Option OptionProps; typedef struct { OptionProps props; - int8_t hasBeenParsed; + bool hasBeenParsed; } Option; static int16_t validateNoDuplicateIds( @@ -114,7 +116,7 @@ static int16_t initOpts( } Option tmp = { - .hasBeenParsed = 0 + .hasBeenParsed = false }; tmp.props = options[i]; /* Cannot assign in aggregate initializer above - causes Visual Studio warning */ @@ -130,30 +132,30 @@ static int16_t initOpts( return 0; } -static int8_t stringLooksLikeOption( +static bool stringLooksLikeOption( const char *str ) { if ( ( str[0] == '-' ) && !is_number( str ) ) { - return 1; + return true; } - return 0; + return false; } -static int8_t optionMatchesString( +static bool optionMatchesString( Option opt, const char *str ) { if ( !stringLooksLikeOption( str ) ) { - return 0; + return false; } if ( strnlen( str, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) { /* String longer than longest possible option - not a match */ - return 0; + return false; } char str_to_upper[MAX_OPTION_LENGTH + 1]; @@ -165,7 +167,7 @@ static int8_t optionMatchesString( to_upper( match_to_upper ); if ( strcmp( str_to_upper, match_to_upper ) == 0 ) { - return 1; + return true; } if ( opt.props.matchShort != NULL ) @@ -175,11 +177,11 @@ static int8_t optionMatchesString( if ( strcmp( str_to_upper, match_to_upper ) == 0 ) { - return 1; + return true; } } - return 0; + return false; } static int16_t parseOpts( @@ -257,7 +259,7 @@ static int16_t parseOpts( { return -1; } - currOpt->hasBeenParsed = 1; + currOpt->hasBeenParsed = true; } currOpt = nextOpt; @@ -275,7 +277,7 @@ static int16_t parseOpts( { return -1; } - currOpt->hasBeenParsed = 1; + currOpt->hasBeenParsed = true; } /* Check mandatory options */ -- GitLab From 7c2523619f5b3f3fb3f9655a0172295205ad4d07 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Fri, 12 Dec 2025 15:56:59 +0100 Subject: [PATCH 20/34] CmdLnParser: Print whitespace using fprintf --- lib_util/cmdln_parser.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index dece415b4d..a91f1e5f0e 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -345,17 +345,6 @@ static int32_t totalNumOptChars( return len; } -static void printWhitespace( - const int32_t n ) -{ - assert( n >= 0 ); - - for ( int32_t i = 0; i < n; ++i ) - { - fprintf( stderr, " " ); - } -} - static void printOptDescriptionAligned( const char *descPtr, int32_t descriptionColumnIdx ) @@ -377,7 +366,7 @@ static void printOptDescriptionAligned( fprintf( stderr, "%c", *descPtr ); if ( *descPtr == '\n' ) { - printWhitespace( descriptionColumnIdx ); + fprintf( stderr, "%*s", descriptionColumnIdx, "" ); } ++descPtr; } @@ -423,8 +412,7 @@ static void printOptions( if ( opt.description != NULL ) { /* Done printing options column, fill with whitespace until description column */ - printWhitespace( descriptionColumnIdx - numOptChars - 2 /* account for ": " below */ ); - fprintf( stderr, ": " ); + fprintf( stderr, "%*s", descriptionColumnIdx - numOptChars, ": " ); printOptDescriptionAligned( opt.description, descriptionColumnIdx ); } else -- GitLab From 5b95c6dbcdc7f10971f9fb2e689ef8cff425c860 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Mon, 15 Dec 2025 10:39:12 +0100 Subject: [PATCH 21/34] CmdLnParser: Improve error handling - avoids dereferencing NULL when parser is misconfigured --- lib_util/cmdln_parser.c | 116 +++++++++++++++++++++++++++------------- lib_util/cmdln_parser.h | 2 +- 2 files changed, 81 insertions(+), 37 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index a91f1e5f0e..e97f2c3ace 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -51,7 +51,14 @@ typedef struct bool hasBeenParsed; } Option; -static int16_t validateNoDuplicateIds( +/* Error enum for internal use */ +typedef enum { + CMDLN_PARSER_ERR_OK = 0, + CMDLN_PARSER_ERR_FAILED_PARSING = -1, + CMDLN_PARSER_ERR_MISCONFIGURED = -2, +} CmdLnParserError; + +static CmdLnParserError validateNoDuplicateIds( const OptionProps *props, int32_t numOpts ) { @@ -62,57 +69,70 @@ static int16_t validateNoDuplicateIds( if ( props[i].id == props[j].id ) { fprintf( stderr, "[dev] Duplicate ID == %d between options %s and %s\n", props[i].id, props[i].match, props[j].match ); - return -1; + return CMDLN_PARSER_ERR_MISCONFIGURED; } } } - return 0; + return CMDLN_PARSER_ERR_OK; } -static int16_t validateOptionProps( +static CmdLnParserError validateOptionProps( OptionProps props ) { /* Check required properties */ if ( props.match == NULL ) { - /* TODO(sgi): Don't print out usage after this - props.match is used there */ fprintf( stderr, "[dev] Option with ID %d - missing required property \"match\"\n", props.id ); - return -1; + return CMDLN_PARSER_ERR_MISCONFIGURED; } if ( props.id == 0 ) { fprintf( stderr, "[dev] Invalid ID for option %s. ID == %d is reserved.\n", props.match, props.id ); - return -1; + return CMDLN_PARSER_ERR_MISCONFIGURED; } /* Check match string length */ if ( strnlen( props.match, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { fprintf( stderr, "[dev] Option with ID %d - match string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); - return -1; + return CMDLN_PARSER_ERR_MISCONFIGURED; } if ( props.matchShort != NULL && strnlen( props.matchShort, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { fprintf( stderr, "[dev] Option with ID %d - matchShort string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); - return -1; + return CMDLN_PARSER_ERR_MISCONFIGURED; } - return 0; + return CMDLN_PARSER_ERR_OK; } /* Validate given OptionProps and use them to initialize array of Options */ -static int16_t initOpts( +static CmdLnParserError initOpts( const OptionProps *options, const int32_t numOpts, Option *opts ) { + CmdLnParserError error; + + if ( numOpts > MAX_SUPPORTED_OPTS ) + { + fprintf( stderr, "[dev] Number of defined options (%d) exceeds limit (%d).\n", numOpts, MAX_SUPPORTED_OPTS ); + return CMDLN_PARSER_ERR_MISCONFIGURED; + } + + /* Check for duplicate IDs */ + if ( ( error = validateNoDuplicateIds( options, numOpts ) ) != 0 ) + { + return error; + } + for ( int32_t i = 0; i < numOpts; ++i ) { - if ( validateOptionProps( options[i] ) != 0 ) + if ( ( error = validateOptionProps( options[i] ) ) != CMDLN_PARSER_ERR_OK ) { - return -1; + return error; } Option tmp = { @@ -123,12 +143,6 @@ static int16_t initOpts( opts[i] = tmp; } - /* Check for duplicate IDs */ - if ( validateNoDuplicateIds( options, numOpts ) != 0 ) - { - return -1; - } - return 0; } @@ -184,7 +198,7 @@ static bool optionMatchesString( return false; } -static int16_t parseOpts( +static CmdLnParserError parseOpts( int32_t argc, char **argv, Option *opts, @@ -220,7 +234,7 @@ static int16_t parseOpts( } fprintf( stderr, "\n" ); - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } break; @@ -234,7 +248,7 @@ static int16_t parseOpts( if ( stringLooksLikeOption( argv[argIdx] ) ) { fprintf( stderr, "Unknown option `%s`\n", argv[argIdx] ); - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } /* Otherwise, value following current option. @@ -246,7 +260,7 @@ static int16_t parseOpts( else { fprintf( stderr, "Unexpected token `%s`\n", argv[argIdx] ); - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } } @@ -257,7 +271,7 @@ static int16_t parseOpts( { if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) { - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } currOpt->hasBeenParsed = true; } @@ -275,7 +289,7 @@ static int16_t parseOpts( { if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) { - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } currOpt->hasBeenParsed = true; } @@ -300,10 +314,10 @@ static int16_t parseOpts( } if ( missingMandatory ) { - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } - return 0; + return CMDLN_PARSER_ERR_OK; } static const char *getBasename( @@ -471,34 +485,64 @@ int16_t CmdLnParser_parseArgs( void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption ) { - assert( numOptions <= MAX_SUPPORTED_OPTS ); + CmdLnParserError error; /* Prepare option array */ Option opts[MAX_SUPPORTED_OPTS]; - if ( initOpts( optionProps, numOptions, opts ) != 0 ) + if ( ( error = initOpts( optionProps, numOptions, opts ) ) != CMDLN_PARSER_ERR_OK ) { goto fail; } /* Iterate over argv and parse */ - if ( parseOpts( argc, argv, opts, numOptions, pOutputStruct, parseOption ) != 0 ) + if ( ( error = parseOpts( argc, argv, opts, numOptions, pOutputStruct, parseOption ) ) != CMDLN_PARSER_ERR_OK ) { goto fail; } - return 0; - fail: - printUsage( argv[0], optionProps, numOptions ); - return -1; + switch (error) + { + case CMDLN_PARSER_ERR_OK: + break; + case CMDLN_PARSER_ERR_MISCONFIGURED: + fprintf( stderr, "CmdLnParser is misconfigured.\n" ); + break; + case CMDLN_PARSER_ERR_FAILED_PARSING: + printUsage( argv[0], optionProps, numOptions ); + break; + } + + return error; } -void CmdLnParser_printUsage( +int16_t CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions ) { + CmdLnParserError error; + + /* Re-use initOpts for validation only */ + Option opts[MAX_SUPPORTED_OPTS]; + if ( ( error = initOpts( options, numOptions, opts ) ) != CMDLN_PARSER_ERR_OK ) + { + goto fail; + } + printUsage( executableName, options, numOptions ); - return; +fail: + switch (error) + { + case CMDLN_PARSER_ERR_OK: + break; + case CMDLN_PARSER_ERR_MISCONFIGURED: + fprintf( stderr, "CmdLnParser is misconfigured.\n" ); + break; + case CMDLN_PARSER_ERR_FAILED_PARSING: + break; + } + + return error; } diff --git a/lib_util/cmdln_parser.h b/lib_util/cmdln_parser.h index ddb16c01d6..c6848f79b8 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -53,6 +53,6 @@ typedef int16_t ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, char **opt int16_t CmdLnParser_parseArgs( int32_t argc, char **argv, const CmdLnParser_Option *options, const int32_t numOptions, void *pOutputStruct, CmdLnParser_FnPtr_ParseOption parseOption ); -void CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions ); +int16_t CmdLnParser_printUsage( char *executableName, const CmdLnParser_Option *options, const int32_t numOptions ); #endif /* CMDLN_PARSER_H */ -- GitLab From cfc2ada7dfe2be786a7987561ae8f46e2eb65485 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Mon, 15 Dec 2025 12:04:51 +0100 Subject: [PATCH 22/34] CmdLnParser: Do not use strnlen - it is not always available --- lib_util/cmdl_tools.c | 25 +++++++++++++++++++++++++ lib_util/cmdl_tools.h | 2 ++ lib_util/cmdln_parser.c | 13 +++++++------ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib_util/cmdl_tools.c b/lib_util/cmdl_tools.c index 625db12733..1f551f8917 100644 --- a/lib_util/cmdl_tools.c +++ b/lib_util/cmdl_tools.c @@ -225,3 +225,28 @@ bool isEmptyString( { return str[0] == '\0'; } + + +/*---------------------------------------------------------------------* + * strLength() + * + * Get the length of a string up to a maximum length. + * + * Equivalent to strnlen, which is not part of the C standard and only provided by POSIX. + * + *---------------------------------------------------------------------*/ + +int32_t strLength( + const char *str, + int32_t maxlen ) +{ + int32_t len; + + len = 0; + while ( len < maxlen && str[len] != '\0' ) + { + len++; + } + + return len; +} diff --git a/lib_util/cmdl_tools.h b/lib_util/cmdl_tools.h index 327acb93d0..b331dea03d 100644 --- a/lib_util/cmdl_tools.h +++ b/lib_util/cmdl_tools.h @@ -50,4 +50,6 @@ void clearString( char *str ); bool isEmptyString( const char *str ); +int32_t strLength( const char *str, int32_t maxlen ); + #endif /* CMDL_TOOLS_H */ diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index e97f2c3ace..a1d6edcd75 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -52,7 +52,8 @@ typedef struct } Option; /* Error enum for internal use */ -typedef enum { +typedef enum +{ CMDLN_PARSER_ERR_OK = 0, CMDLN_PARSER_ERR_FAILED_PARSING = -1, CMDLN_PARSER_ERR_MISCONFIGURED = -2, @@ -94,12 +95,12 @@ static CmdLnParserError validateOptionProps( } /* Check match string length */ - if ( strnlen( props.match, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) + if ( strLength( props.match, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { fprintf( stderr, "[dev] Option with ID %d - match string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); return CMDLN_PARSER_ERR_MISCONFIGURED; } - if ( props.matchShort != NULL && strnlen( props.matchShort, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) + if ( props.matchShort != NULL && strLength( props.matchShort, MAX_OPTION_MATCH_LENGTH + 1 ) > MAX_OPTION_MATCH_LENGTH ) { fprintf( stderr, "[dev] Option with ID %d - matchShort string exceeds limit of %d characters.\n", props.id, MAX_OPTION_MATCH_LENGTH ); return CMDLN_PARSER_ERR_MISCONFIGURED; @@ -426,7 +427,7 @@ static void printOptions( if ( opt.description != NULL ) { /* Done printing options column, fill with whitespace until description column */ - fprintf( stderr, "%*s", descriptionColumnIdx - numOptChars, ": " ); + fprintf( stderr, "%*s", descriptionColumnIdx - numOptChars, ": " ); printOptDescriptionAligned( opt.description, descriptionColumnIdx ); } else @@ -501,7 +502,7 @@ int16_t CmdLnParser_parseArgs( } fail: - switch (error) + switch ( error ) { case CMDLN_PARSER_ERR_OK: break; @@ -533,7 +534,7 @@ int16_t CmdLnParser_printUsage( printUsage( executableName, options, numOptions ); fail: - switch (error) + switch ( error ) { case CMDLN_PARSER_ERR_OK: break; -- GitLab From 8cf192bcaa5768ad655e4ae918e79b600e608cf7 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Mon, 15 Dec 2025 12:41:34 +0100 Subject: [PATCH 23/34] CmdLnParser: Remove one more use of strnlen --- lib_util/cmdln_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index a1d6edcd75..cd201a0cdc 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -167,7 +167,7 @@ static bool optionMatchesString( return false; } - if ( strnlen( str, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + if ( strLength( str, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) { /* String longer than longest possible option - not a match */ return false; -- GitLab From 0fb8872aedc53d439637f5e8e4714d1e37b4c0d9 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 16 Dec 2025 13:41:10 +0100 Subject: [PATCH 24/34] change ISAR_post_rend command-line description --- readme.txt | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/readme.txt b/readme.txt index 87f84ab550..7f8954dbea 100644 --- a/readme.txt +++ b/readme.txt @@ -384,27 +384,28 @@ Options: The usage of the "ISAR_post_rend" program is as follows: -------------------------------------------------------- -Usage: ISAR_post_rend [options] +Usage: ISAR_post_rend [options] -i -if -o Mandatory parameters: --------------------- - --i File : Input Audio File (input file is bitstream if format is BINAURAL_SPLIT_CODED, or PCM/WAV file if format is BINAURAL_SPLIT_PCM) --if Format : Format of Input File (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ...); Use -l to get a list of supported formats --o File : Output Audio File in BINAURAL format +--input_file, -i : Path to the input file (WAV or raw PCM file with BINAURAL_SPLIT_PCM input format or ISAR bitstream file with BINAURAL_SPLIT_CODED input format) +--input_format, -if : Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list) +--output_file, -o : Path to the output file Options: -------- --im Files : Input Metadata File for BINAURAL_SPLIT_PCM format --fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs --T File : Head rotation trajectory file for simulation of head tracking --prbfi File : Split rendering BFI (Bad Frame Indicator) file --no_delay_cmp : Turn off delay compensation --level : Complexity level, level = (1, 2, 3), will be defined after characterisation. --q : Quiet mode, limit printouts to terminal, default is deactivated --l : List supported audio formats --fr : Set audio rendering frame size --s File : Path to the split rendering init params file +--input_metadata, -im : Path to the input metadata file for BINAURAL_SPLIT_PCM input format +--sample_rate, -fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM input files +--trajectory_file, -T : Head rotation trajectory file for simulation of head tracking +--post_rend_bfi_file, -prbfi : Split rendering BFI (Bad Frame Indicator) file +--no_delay_compensation, -no_delay_cmp : Turn off delay compensation +--complexity_level, -level : Complexity level (1, 2, 3) - will be defined after characterisation +--quiet, -q : Quiet mode - limit printouts to terminal +--list, -l : List supported audio formats of input file +--framing, -fr : Set audio rendering frame size in ms (5, 10, 20) +--sr_params, -s : Path to the split rendering init params file +--help, -h : Show this help message and exit + The usage of the "ambi_converter" program is as follows: -------------------------------------------------------- -- GitLab From 76795b39c87b65748d7451a1a11f57f670274f04 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 16 Dec 2025 13:47:15 +0100 Subject: [PATCH 25/34] change ISAR_post_rend command-line description --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 7f8954dbea..853fec87c3 100644 --- a/readme.txt +++ b/readme.txt @@ -402,7 +402,7 @@ Options: --complexity_level, -level : Complexity level (1, 2, 3) - will be defined after characterisation --quiet, -q : Quiet mode - limit printouts to terminal --list, -l : List supported audio formats of input file ---framing, -fr : Set audio rendering frame size in ms (5, 10, 20) +--framing, -fr : Render frame size in ms (5, 10, 20), default is 20 --sr_params, -s : Path to the split rendering init params file --help, -h : Show this help message and exit -- GitLab From 9a7751d4e66dc9cd989c40d081b3ec63ef3f33b3 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 16 Dec 2025 16:25:14 +0100 Subject: [PATCH 26/34] print mandatory options as part of Usage: ... string --- lib_util/cmdln_parser.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index cd201a0cdc..dc0064f19e 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -442,10 +442,6 @@ static void printUsage( const OptionProps *optionProps, const int32_t numOptions ) { - fprintf( stderr, "\n" ); - fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) ); - fprintf( stderr, "\n" ); - int32_t numOptChars; int32_t maxNumOptChars = 0; bool hasMandatoryOptions = false; @@ -465,6 +461,28 @@ static void printUsage( } } + fprintf( stderr, "\n" ); + fprintf( stderr, "Usage: %s [options]", getBasename( argv0 ) ); + for ( int32_t i = 0; i < numOptions; ++i ) + { + OptionProps opt = optionProps[i]; + + if ( opt.isMandatory ) + { + if (!isEmptyString(opt.matchShort)) + { + fprintf( stderr, " -%s", opt.matchShort ); + } + + if ( !isEmptyString( opt.placeholder ) ) + { + fprintf( stderr, " %s", opt.placeholder ); + } + } + } + + fprintf( stderr, "\n\n" ); + if ( hasMandatoryOptions ) { fprintf( stderr, "Mandatory parameters:\n---------------------\n" ); -- GitLab From 3db57156a6cefa7dc318fd53868b6f91ffed231d Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 16 Dec 2025 16:26:16 +0100 Subject: [PATCH 27/34] change ISAR params description --- apps/isar_post_rend.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index c67b21425c..eec16daab2 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -139,7 +139,7 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_file", .matchShort = "i", .placeholder = "", - .description = "Path to the input file (WAV or raw PCM with BINAURAL_SPLIT_PCM input format, coded ISAR file with BINAURAL_SPLIT_CODED)", + .description = "Path to the input file (WAV or raw PCM file with BINAURAL_SPLIT_PCM input format or ISAR bitstream file with BINAURAL_SPLIT_CODED input format)", .isMandatory = true, }, { @@ -163,14 +163,14 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_metadata", .matchShort = "im", .placeholder = "", - .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input mode", + .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input format", }, { .id = CmdLnOptionId_sampleRate, .match = "sample_rate", .matchShort = "fs", .placeholder = "", - .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs", + .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM input files", }, { .id = CmdLnOptionId_trajFile, @@ -197,7 +197,7 @@ static const CmdLnParser_Option cliOptions[] = { .match = "complexity_level", .matchShort = "level", .placeholder = "", - .description = "Complexity level, level = (1, 2, 3), will be defined after characterisation.", + .description = "Complexity level (1, 2, 3) - will be defined after characterisation", }, { .id = CmdLnOptionId_quietModeEnabled, @@ -209,7 +209,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_listFormats, .match = "list", .matchShort = "l", - .description = "List supported audio formats", + .description = "List supported audio formats of input file", }, { .id = CmdLnOptionId_framing, -- GitLab From 8814aa8ad20a4c4faed0438aa22c8b46dd957565 Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Wed, 17 Dec 2025 10:53:36 +0100 Subject: [PATCH 28/34] set default head pose in case -T head_rot_file is not provided --- apps/isar_post_rend.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index eec16daab2..d56c5889db 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -1299,36 +1299,46 @@ int main( /* Convert from int to float and from interleaved to packed */ convertInputBuffer( inpInt16Buffer, numSamplesRead, inBuffer.config.numSamplesPerChannel, num_in_channels, inFloatBuffer ); - int16_t num_subframes, sf_idx; + int16_t num_subframes; num_subframes = (int16_t) args.render_framesize; /* Read from head rotation trajectory file if specified */ + IVAS_QUATERNION headRot[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; + IVAS_VECTOR3 Pos[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; if ( headRotReader != NULL ) { - for ( sf_idx = 0; sf_idx < num_subframes; sf_idx++ ) + for ( i = 0; i < num_subframes; i++ ) { - IVAS_QUATERNION headRot; - IVAS_VECTOR3 Pos; - - if ( ( error = HeadRotationFileReading( headRotReader, &headRot, &Pos ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReading( headRotReader, &headRot[i], &Pos[i] ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in Head Rotation File Reading: %s\n", ivas_error_to_string( error ) ); goto cleanup; } - - if ( ( error = ISAR_POST_REND_SetHeadRotation( hIsarPostRend, headRot, Pos, DEFAULT_AXIS, sf_idx ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError setting Head Rotation: %s\n", ivas_error_to_string( error ) ); - goto cleanup; - } } } else { - fprintf( stderr, "\nHead Rotation should be enabled in post renderer\n" ); - goto cleanup; + for ( i = 0; i < num_subframes; i++ ) + { + headRot[i].w = -3.0f; + headRot[i].x = 0.0f; + headRot[i].y = 0.0f; + headRot[i].z = 0.0f; + Pos[i].x = 0.0f; + Pos[i].y = 0.0f; + Pos[i].z = 0.0f; + } } + for ( i = 0; i < num_subframes; i++ ) + { + if ( ( error = ISAR_POST_REND_SetHeadRotation( hIsarPostRend, headRot[i], Pos[i], DEFAULT_AXIS, i ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError setting Head Rotation: %s\n", ivas_error_to_string( error ) ); + goto cleanup; + } + } + /* Read from split renderer bfi file if specified */ if ( splitRendBFIReader != NULL && splitBinNeedsNewFrame ) { -- GitLab From 2e1deaf8968d545510d87d7a07d63cec7ab4bc61 Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Wed, 17 Dec 2025 11:01:09 +0100 Subject: [PATCH 29/34] do not overwrite arg.enableHeadRotation in case of ISAR when no -T head_rot_file is provided --- apps/decoder.c | 37 ++++++++++++++++++++++--------------- lib_dec/lib_dec.c | 5 +++++ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index f6b55ba00f..e698a357f5 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -448,7 +448,6 @@ int main( asked_frame_size = arg.renderFramesize; uint16_t aeID = arg.aeSequence.count > 0 ? arg.aeSequence.pID[0] : IVAS_DEFAULT_AEID; - arg.enableHeadRotation = arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM; if ( ( error = IVAS_DEC_Configure( hIvasDec, arg.output_Fs, arg.outputConfig, arg.renderFramesize, arg.customLsOutputEnabled, arg.hrtfReaderEnabled, arg.enableHeadRotation, arg.enableExternalOrientation, arg.orientation_tracking, arg.renderConfigEnabled, arg.roomSize, arg.non_diegetic_pan_enabled, @@ -2235,6 +2234,7 @@ static ivas_error decodeG192( #endif #endif IsmFileWriter *ismWriters[IVAS_MAX_NUM_OBJECTS]; + IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 } }; IVAS_VECTOR3 Pos[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; SplitFileReadWrite *splitRendWriter = NULL; @@ -2422,10 +2422,8 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; - if ( headRotReader == NULL ) { for ( i = 0; i < num_subframes; i++ ) @@ -2463,7 +2461,6 @@ static ivas_error decodeG192( if ( arg.enableExternalOrientation ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableHeadRotation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableExternalOrientation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableRotationInterpolation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; @@ -2850,16 +2847,28 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { - IVAS_QUATERNION Quaternion; - if ( ( error = HeadRotationFileReading( headRotReader, &Quaternion, &Pos[0] ) ) != IVAS_ERR_OK ) + if ( headRotReader == NULL ) { - fprintf( stderr, "\nError %s while reading head orientation from %s\n", IVAS_DEC_GetErrorMessage( error ), RotationFileReader_getFilePath( headRotReader ) ); - goto cleanup; + Quaternions[0].w = -3.0f; + Quaternions[0].x = 0.0f; + Quaternions[0].y = 0.0f; + Quaternions[0].z = 0.0f; + Pos[0].x = 0.0f; + Pos[0].y = 0.0f; + Pos[0].z = 0.0f; + } + else + { + if ( ( error = HeadRotationFileReading( headRotReader, &Quaternions[0], &Pos[0] ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while reading head orientation from %s\n", IVAS_DEC_GetErrorMessage( error ), RotationFileReader_getFilePath( headRotReader ) ); + goto cleanup; + } } - if ( ( error = IVAS_DEC_FeedHeadTrackData( hIvasDec, Quaternion, Pos[0], 0, DEFAULT_AXIS ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_DEC_FeedHeadTrackData( hIvasDec, Quaternions[0], Pos[0], 0, DEFAULT_AXIS ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_DEC_FeedHeadTrackData failed: %s\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; @@ -3182,6 +3191,7 @@ static ivas_error decodeVoIP( IVAS_DEC_BS_FORMAT bsFormat = IVAS_DEC_BS_UNKOWN; IsmFileWriter *ismWriters[IVAS_MAX_NUM_OBJECTS]; + IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 } }; IVAS_VECTOR3 Pos[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; int16_t nOutSamples = 0; @@ -3384,10 +3394,8 @@ static ivas_error decodeVoIP( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; - if ( headRotReader == NULL ) { for ( i = 0; i < num_subframes; i++ ) @@ -3426,7 +3434,6 @@ static ivas_error decodeVoIP( if ( arg.enableExternalOrientation ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableHeadRotation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableExternalOrientation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; int8_t enableRotationInterpolation[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 102c73becc..f09d69dfde 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -519,6 +519,11 @@ ivas_error IVAS_DEC_Configure( hDecoderConfig->Opt_aeid_on = acousticEnvironmentId != 65535 ? TRUE : FALSE; hDecoderConfig->Opt_ObjEdit_on = (int16_t) objEditEnabled; + if ( outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) + { + hDecoderConfig->Opt_Headrotation = 1; + } + if ( renderFramesize == IVAS_RENDER_FRAMESIZE_UNKNOWN ) { return IVAS_ERR_WRONG_PARAMS; -- GitLab From 04603fc2a72bd4d1735f28ab0389739f7e6bc390 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 17 Dec 2025 11:16:37 +0100 Subject: [PATCH 30/34] clang format --- apps/isar_post_rend.c | 8 +++----- lib_isar/lib_isar_post_rend.h | 2 +- lib_util/cmdln_parser.c | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index d56c5889db..b6ce86bc8d 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -406,8 +406,6 @@ static IVAS_AUDIO_CONFIG parseAudioConfig( } - - static CmdlnArgs defaultArgs( const char *executableName ) { @@ -461,7 +459,7 @@ static int16_t parseOption( fprintf( stderr, "Error: No input file has been provided!\n" ); return -1; } - else if (numOptionValues > 1) + else if ( numOptionValues > 1 ) { fprintf( stderr, "Error: The program expects a single input file, but %d have been provided!\n", numOptionValues ); return -1; @@ -492,7 +490,7 @@ static int16_t parseOption( fprintf( stderr, "Error: No input metadata file for BINAURAL_SPLIT_PCM mode has been provided!\n" ); return -1; } - else if (numOptionValues > 1) + else if ( numOptionValues > 1 ) { fprintf( stderr, "Error: The program expects a single input metadata file, but %d have been provided!\n", numOptionValues ); return -1; @@ -1338,7 +1336,7 @@ int main( goto cleanup; } } - + /* Read from split renderer bfi file if specified */ if ( splitRendBFIReader != NULL && splitBinNeedsNewFrame ) { diff --git a/lib_isar/lib_isar_post_rend.h b/lib_isar/lib_isar_post_rend.h index 8a2378a0a4..7d77220506 100644 --- a/lib_isar/lib_isar_post_rend.h +++ b/lib_isar/lib_isar_post_rend.h @@ -41,7 +41,7 @@ * ISAR post-renderer constants *---------------------------------------------------------------------*/ -#define RENDERER_MAX_BIN_INPUTS 1 +#define RENDERER_MAX_BIN_INPUTS 1 /*---------------------------------------------------------------------* * ISAR post-renderer structures diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index dc0064f19e..3bd9ffecc8 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -469,7 +469,7 @@ static void printUsage( if ( opt.isMandatory ) { - if (!isEmptyString(opt.matchShort)) + if ( !isEmptyString( opt.matchShort ) ) { fprintf( stderr, " -%s", opt.matchShort ); } -- GitLab From dff22d9cee6abff119562cfe294adc816e9e2f68 Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Wed, 17 Dec 2025 13:18:42 +0100 Subject: [PATCH 31/34] check uninitialized parser params against NULL instead of an empty string --- lib_util/cmdln_parser.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib_util/cmdln_parser.c b/lib_util/cmdln_parser.c index 3bd9ffecc8..5616438649 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -469,12 +469,16 @@ static void printUsage( if ( opt.isMandatory ) { - if ( !isEmptyString( opt.matchShort ) ) + if ( opt.matchShort != NULL ) { fprintf( stderr, " -%s", opt.matchShort ); } + else + { + fprintf( stderr, " --%s", opt.match ); + } - if ( !isEmptyString( opt.placeholder ) ) + if ( opt.placeholder != NULL ) { fprintf( stderr, " %s", opt.placeholder ); } -- GitLab From 9b717bf0d33792bed06884dfc7764a7dcfb38828 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 17 Dec 2025 13:19:34 +0100 Subject: [PATCH 32/34] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: sagnowski --- apps/isar_post_rend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index b6ce86bc8d..4d2b2c0d15 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -139,7 +139,7 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_file", .matchShort = "i", .placeholder = "", - .description = "Path to the input file (WAV or raw PCM file with BINAURAL_SPLIT_PCM input format or ISAR bitstream file with BINAURAL_SPLIT_CODED input format)", + .description = "Path to the input file (WAV or raw PCM file with BINAURAL_SPLIT_PCM input format\nor ISAR bitstream file with BINAURAL_SPLIT_CODED input format)", .isMandatory = true, }, { -- GitLab From 92594ed8c2125e3f6573f407286a0d613005912b Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Wed, 17 Dec 2025 16:02:19 +0100 Subject: [PATCH 33/34] use isSplitRend instead of the combined condition --- apps/decoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index e698a357f5..40bc7d9df0 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -2422,7 +2422,7 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) + if ( arg.enableHeadRotation || isSplitRend ) { if ( headRotReader == NULL ) { @@ -2847,7 +2847,7 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) + if ( arg.enableHeadRotation || isSplitRend ) { if ( headRotReader == NULL ) { @@ -3394,7 +3394,7 @@ static ivas_error decodeVoIP( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) + if ( arg.enableHeadRotation || isSplitRend ) { if ( headRotReader == NULL ) { -- GitLab From e3b32aaa7b21625e65577069eb76ef5a8ed31f37 Mon Sep 17 00:00:00 2001 From: "Malenovsky, Vladimir" Date: Wed, 17 Dec 2025 16:02:44 +0100 Subject: [PATCH 34/34] remove unused CmdLnOptionId_orientationTracking --- apps/isar_post_rend.c | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index b6ce86bc8d..a396f15f5f 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -121,7 +121,6 @@ typedef enum CmdLnOptionId_outputFile, CmdLnOptionId_sampleRate, CmdLnOptionId_trajFile, - CmdLnOptionId_orientationTracking, CmdLnOptionId_complexityLevel, CmdLnOptionId_noDelayCmp, CmdLnOptionId_quietModeEnabled, -- GitLab