From 02d2fec411d57ec2c1fde38212f1f4d0b1595de0 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 13 Jan 2026 16:33:05 +0100 Subject: [PATCH 1/9] fixing ISAR command-line issues --- apps/decoder.c | 37 ++- apps/isar_post_rend.c | 357 +++++++++++++---------- apps/renderer.c | 91 +++--- lib_dec/lib_dec_fx.c | 6 + lib_isar/lib_isar_post_rend.h | 1 - lib_rend/ivas_output_init_fx.c | 4 +- lib_util/cmdl_tools.c | 26 ++ lib_util/cmdl_tools.h | 2 + lib_util/cmdln_parser.c | 369 +++++++++++++++++------- lib_util/cmdln_parser.h | 16 +- lib_util/split_render_file_read_write.c | 6 +- readme.txt | 25 +- 12 files changed, 602 insertions(+), 338 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 494f76ccb..4e1bfc1a6 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -417,7 +417,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, @@ -2021,6 +2020,7 @@ static ivas_error decodeG192( bool needNewFrame; int16_t nSamplesRendered, nSamplesRendered_loop, nSamplesToRender; 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, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; SplitFileReadWrite *splitRendWriter = NULL; @@ -2169,10 +2169,8 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || isSplitRend ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; - if ( headRotReader == NULL ) { for ( i = 0; i < num_subframes; i++ ) @@ -2212,7 +2210,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]; @@ -2559,16 +2556,28 @@ static ivas_error decodeG192( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || isSplitRend ) { - 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; @@ -2800,6 +2809,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, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; int16_t nOutSamples = 0; @@ -3000,10 +3010,8 @@ static ivas_error decodeVoIP( } /* Head-tracking input simulation */ - if ( arg.enableHeadRotation ) + if ( arg.enableHeadRotation || isSplitRend ) { - IVAS_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES]; - if ( headRotReader == NULL ) { for ( i = 0; i < num_subframes; i++ ) @@ -3043,7 +3051,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/apps/isar_post_rend.c b/apps/isar_post_rend.c index 6ccf204ce..e6e45683b 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -106,14 +106,12 @@ 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; bool delayCompensationEnabled; bool quietModeEnabled; - bool sceneDescriptionInput; IVAS_RENDER_FRAMESIZE render_framesize; } CmdlnArgs; @@ -124,7 +122,6 @@ typedef enum CmdLnOptionId_outputFile, CmdLnOptionId_sampleRate, CmdLnOptionId_trajFile, - CmdLnOptionId_orientationTracking, CmdLnOptionId_complexityLevel, CmdLnOptionId_noDelayCmp, CmdLnOptionId_quietModeEnabled, @@ -133,6 +130,7 @@ typedef enum CmdLnOptionId_SplitRendBFIFile, CmdLnOptionId_framing, CmdLnOptionId_srParamsFile, + CmdLnOptionId_help, } CmdLnOptionId; static const CmdLnParser_Option cliOptions[] = { @@ -140,80 +138,99 @@ 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)", + .placeholder = "", + .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, }, { .id = CmdLnOptionId_inputFormat, .match = "input_format", .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 path to metadata files for BINAURAL_SPLIT_PCM input mode", + .placeholder = "", + .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", + .placeholder = "", .description = "Path to the output file", + .isMandatory = true, + }, + { + .id = CmdLnOptionId_inputMetadata, + .match = "input_metadata", + .matchShort = "im", + .placeholder = "", + .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input format", }, { .id = CmdLnOptionId_sampleRate, .match = "sample_rate", .matchShort = "fs", - .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM inputs", + .placeholder = "", + .description = "Input sampling rate in kHz (16, 32, 48) - required only with raw PCM input files", }, { .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", - .description = "Split rendering option: bfi file", + .placeholder = "", + .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, .match = "complexity_level", .matchShort = "level", - .description = "Complexity level, level = (1, 2, 3), will be defined after characterisation.", + .placeholder = "", + .description = "Complexity level (1, 2, 3) - will be defined after characterisation", }, { .id = CmdLnOptionId_quietModeEnabled, .match = "quiet", .matchShort = "q", - .description = "[flag] Limit printouts to terminal", + .description = "Quiet mode - limit printouts to terminal", }, { .id = CmdLnOptionId_listFormats, .match = "list", .matchShort = "l", - .description = "List supported audio formats", + .description = "List supported audio formats of input file", }, { .id = CmdLnOptionId_framing, .match = "framing", .matchShort = "fr", - .description = "Set Render audio framing.", + .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", }, + { + .id = CmdLnOptionId_help, + .match = "help", + .matchShort = "h", + .description = "Show this help message and exit", + }, }; @@ -293,26 +310,12 @@ 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 ); @@ -331,7 +334,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; } } @@ -405,50 +408,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 required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - - const bool singleInputSpecified = ( args.inConfig.numBinBuses != 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.sampleRate == 0 ) - { - tmpOption = findOptionById( CmdLnOptionId_sampleRate ); - fprintf( stderr, "Missing required argument: %s (%s)\n", tmpOption->match, tmpOption->matchShort ); - missingRequiredArg = true; - } - if ( missingRequiredArg ) - { - CmdLnParser_printUsage( args.executableName, cliOptions, numCliOptions ); - } - - return !missingRequiredArg; -} - - static CmdlnArgs defaultArgs( const char *executableName ) { @@ -461,20 +420,21 @@ static CmdlnArgs defaultArgs( args.sampleRate = 0; - args.outConfig.audioConfig = IVAS_AUDIO_CONFIG_INVALID; + 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; - for ( int32_t i = 0; i < RENDERER_MAX_ISAR_MD_INPUTS; ++i ) - { - clearString( args.inMetadataFilePaths[i] ); - } - args.numInMetadataFiles = 0; + args.outConfig.audioConfig = IVAS_AUDIO_CONFIG_INVALID; + clearString( args.inMetadataFilePath ); 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; @@ -482,7 +442,7 @@ static CmdlnArgs defaultArgs( } -static void parseOption( +static int16_t parseOption( const int32_t optionId, char **optionValues, const int16_t numOptionValues, @@ -493,88 +453,177 @@ static void parseOption( switch ( optionId ) { case CmdLnOptionId_listFormats: - assert( numOptionValues == 0 ); printSupportedAudioConfigs(); exit( 0 ); case CmdLnOptionId_inputFile: - assert( 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: - assert( numOptionValues == 1 ); - if ( !parseInConfig( optionValues[0], &args->inConfig, &args->sceneDescriptionInput ) ) + if ( numOptionValues == 0 ) { - exit( -1 ); /* Error printout handled by failing function */ + 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 */ + return -1; } break; case CmdLnOptionId_inputMetadata: - assert( numOptionValues <= RENDERER_MAX_ISAR_MD_INPUTS ); - for ( int16_t i = 0; i < numOptionValues; ++i ) + if ( numOptionValues == 0 ) + { + fprintf( stderr, "Error: No input metadata file for BINAURAL_SPLIT_PCM mode has been provided!\n" ); + return -1; + } + 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: - assert( 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: - assert( numOptionValues == 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 == 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: - assert( numOptionValues == 1 ); + if ( numOptionValues == 0 ) + { + fprintf( stderr, "Error: No BFI file has been provided!\n" ); + return -1; + } + else if ( numOptionValues > 1 ) + { + 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: - assert( 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 ) { - 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 == 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, "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; case CmdLnOptionId_srParamsFile: - assert( 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: 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 ); break; + case CmdLnOptionId_help: + CmdLnParser_printUsage( args->executableName, cliOptions, numCliOptions ); + exit( 0 ); 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; } @@ -589,11 +638,6 @@ static CmdlnArgs parseCmdlnArgs( exit( -1 ); /* Error printout handled by failing function */ } - if ( !checkRequiredArgs( args ) ) - { - exit( -1 ); /* Error printout handled by failing function */ - } - return args; } @@ -602,13 +646,12 @@ static void printSupportedAudioConfigs( void ) { uint16_t i; const char *supportedFormats[] = { - "BINAURAL (output only)", "BINAURAL_SPLIT_PCM", "BINAURAL_SPLIT_CODED", "RTPDUMP", }; - 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] ); @@ -876,7 +919,6 @@ int main( convert_backslash( args.inputFilePath ); convert_backslash( args.outputFilePath ); - convert_backslash( args.headRotationFilePath ); convert_backslash( args.srParamsFilePath ); /*------------------------------------------------------------------------------------------* @@ -885,6 +927,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 ); @@ -899,7 +942,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; + } } int32_t inFileSampleRate = 0; @@ -908,7 +955,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, @@ -917,13 +964,21 @@ int main( &bitsBuffer.config.lc3plusHighRes ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nCould not open split rend metadata file %s\n", 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.inMetadataFilePath ); + } 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; } } @@ -939,13 +994,13 @@ 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; } if ( ( error = IVAS_RTP_READER_Init( &srRTP, args.inputFilePath, NULL, false, NULL ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "error 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; @@ -966,7 +1021,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; @@ -992,14 +1047,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 raw PCM input!\n" ); goto cleanup; } break; @@ -1014,7 +1069,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; } } @@ -1242,34 +1297,44 @@ int main( /* Convert from int to float and from interleaved to packed */ convertInputBuffer( inpInt16Buffer, numSamplesRead, inBuffer.config.numSamplesPerChannel, num_in_channels, inFloatBuffer_fx ); *inBuffer.pq_fact = 0; - 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 */ diff --git a/apps/renderer.c b/apps/renderer.c index c494ee0f1..51fda9b11 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -242,48 +242,60 @@ 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, }, { .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, }, { .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, }, { .id = CmdLnOptionId_outputFormat, .match = "output_format", .matchShort = "of", + .placeholder = "", .description = "Output format to render.\nAlternatively, can be a custom loudspeaker layout file", + .isMandatory = true, }, { .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", }, #ifndef FIX_1494_SET_SPLITBFI_UNUSED @@ -291,6 +303,7 @@ static const CmdLnParser_Option cliOptions[] = { .id = CmdLnOptionId_SplitRendBFIFile, .match = "post_rend_bfi_file", .matchShort = "prbfi", + .placeholder = "", .description = "Split rendering option: bfi file", }, #endif @@ -298,42 +311,49 @@ static const CmdLnParser_Option cliOptions[] = { .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)", }, { @@ -346,6 +366,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.", }, { @@ -358,6 +379,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", }, { @@ -370,43 +392,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 a text file where each line contains \"ID duration\" for BINAURAL_ROOM_REVERB output configuration.", }, { .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)", } }; @@ -1559,7 +1588,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; } } @@ -2716,53 +2745,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 ); - } - - return !missingRequiredArg; -} - static CmdlnArgs defaultArgs( const char *executableName ) { @@ -2844,7 +2826,7 @@ static CmdlnArgs defaultArgs( return args; } -static void parseOption( +static int16_t parseOption( const int32_t optionId, char **optionValues, const int16_t numOptionValues, @@ -3036,7 +3018,7 @@ static void parseOption( break; } - return; + return 0; } static CmdlnArgs parseCmdlnArgs( @@ -3050,11 +3032,6 @@ static CmdlnArgs parseCmdlnArgs( exit( -1 ); /* Error printout handled by failing function */ } - if ( !checkRequiredArgs( args ) ) - { - exit( -1 ); /* Error printout handled by failing function */ - } - if ( args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || args.outConfig.audioConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { args.Opt_Headrotation = 1; diff --git a/lib_dec/lib_dec_fx.c b/lib_dec/lib_dec_fx.c index c20a69451..0787634b1 100644 --- a/lib_dec/lib_dec_fx.c +++ b/lib_dec/lib_dec_fx.c @@ -622,6 +622,12 @@ ivas_error IVAS_DEC_Configure( move16(); move16(); + IF ( EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + { + hDecoderConfig->Opt_Headrotation = 1; + move16(); + } + IF( EQ_16( renderFramesize, IVAS_RENDER_FRAMESIZE_UNKNOWN ) ) { return IVAS_ERR_WRONG_PARAMS; diff --git a/lib_isar/lib_isar_post_rend.h b/lib_isar/lib_isar_post_rend.h index ba22fba6a..5e5b62082 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 /*---------------------------------------------------------------------* diff --git a/lib_rend/ivas_output_init_fx.c b/lib_rend/ivas_output_init_fx.c index 95a225aba..0377c6984 100644 --- a/lib_rend/ivas_output_init_fx.c +++ b/lib_rend/ivas_output_init_fx.c @@ -615,11 +615,11 @@ ivas_error get_channel_config( } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) ) { - strcpy( (char *) str, "BINAURAL_SPLIT_CODED" ); + strcpy( (char *) str, "Binaural: split-rendering mode using encoded data" ); } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) { - strcpy( (char *) str, "Binaural_Split_PCM" ); + strcpy( (char *) str, "Binaural: split-rendering mode using PCM data" ); } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_EXTERNAL ) ) { diff --git a/lib_util/cmdl_tools.c b/lib_util/cmdl_tools.c index 945bda07d..db848c124 100644 --- a/lib_util/cmdl_tools.c +++ b/lib_util/cmdl_tools.c @@ -226,3 +226,29 @@ 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 327acb93d..b331dea03 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 1b9eb7a92..561643864 100644 --- a/lib_util/cmdln_parser.c +++ b/lib_util/cmdln_parser.c @@ -36,19 +36,30 @@ #include #include #include +#include +#include -#define MAX_SUPPORTED_OPTS ( 1024 ) -#define MAX_OPTION_LENGTH ( 1024 ) +#define MAX_SUPPORTED_OPTS ( 1024 ) +#define MAX_OPTION_MATCH_LENGTH ( 30 ) +#define MAX_OPTION_LENGTH ( MAX_OPTION_MATCH_LENGTH + 2 ) typedef CmdLnParser_Option OptionProps; typedef struct { OptionProps props; - int8_t hasBeenParsed; + 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 ) { @@ -59,121 +70,136 @@ 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; + fprintf( stderr, "[dev] Option with ID %d - missing required property \"match\"\n", props.id ); + 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; } - return 0; + /* Check match string 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 && 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; + } + + 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 = { - .hasBeenParsed = 0 + .hasBeenParsed = false }; tmp.props = options[i]; /* Cannot assign in aggregate initializer above - causes Visual Studio warning */ opts[i] = tmp; } - /* Check for duplicate IDs */ - if ( validateNoDuplicateIds( options, numOpts ) != 0 ) - { - return -1; - } - - return 0; -} - -static int8_t stringLooksLikeOption( - const char *str ) -{ - if ( ( str[0] == '-' ) && is_number( str ) == false ) - { - return 1; - } - return 0; } -static const char *stringToOptionName( +static bool stringLooksLikeOption( const char *str ) { - while ( ( *str == '-' ) && ( ( str[1] != '0' ) || ( str[1] != '1' ) ) ) + if ( ( str[0] == '-' ) && !is_number( str ) ) { - ++str; + return true; } - return str; + return false; } -static int8_t optionMatchesString( +static bool optionMatchesString( Option opt, const char *str ) { if ( !stringLooksLikeOption( str ) ) { - return 0; + return false; } - const char *optionName = stringToOptionName( str ); + if ( strLength( str, MAX_OPTION_LENGTH + 1 ) > MAX_OPTION_LENGTH ) + { + /* String longer than longest possible option - not a match */ + return false; + } - char optionName_to_upper[FILENAME_MAX]; - 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[FILENAME_MAX]; - strncpy( match_to_upper, opt.props.match, sizeof( match_to_upper ) - 1 ); - optionName_to_upper[sizeof( match_to_upper ) - 1] = '\0'; + char match_to_upper[MAX_OPTION_LENGTH + 1]; + snprintf( match_to_upper, sizeof( match_to_upper ), "--%s", opt.props.match ); to_upper( match_to_upper ); + if ( strcmp( str_to_upper, match_to_upper ) == 0 ) + { + return true; + } - 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 ( opt.props.matchShort != NULL ) { - return 1; + snprintf( match_to_upper, sizeof( match_to_upper ), "-%s", opt.props.matchShort ); + to_upper( match_to_upper ); + + if ( strcmp( str_to_upper, match_to_upper ) == 0 ) + { + return true; + } } - return 0; + return false; } -static int16_t parseOpts( +static CmdLnParserError parseOpts( int32_t argc, char **argv, Option *opts, @@ -202,8 +228,14 @@ static int16_t parseOpts( /* Check if already parsed */ if ( optToMatch->hasBeenParsed ) { - fprintf( stderr, "Duplicate option: %s (%s)\n", optToMatch->props.match, optToMatch->props.matchShort ); - return -1; + fprintf( stderr, "Duplicate option provided: --%s", optToMatch->props.match ); + if ( optToMatch->props.matchShort != NULL ) + { + fprintf( stderr, "/-%s", optToMatch->props.matchShort ); + } + fprintf( stderr, "\n" ); + + return CMDLN_PARSER_ERR_FAILED_PARSING; } break; @@ -216,8 +248,8 @@ static int16_t parseOpts( /* Invalid option */ if ( stringLooksLikeOption( argv[argIdx] ) ) { - fprintf( stderr, "Unknown option `%s`\n", stringToOptionName( argv[argIdx] ) ); - return -1; + fprintf( stderr, "Unknown option `%s`\n", argv[argIdx] ); + return CMDLN_PARSER_ERR_FAILED_PARSING; } /* Otherwise, value following current option. @@ -229,7 +261,7 @@ static int16_t parseOpts( else { fprintf( stderr, "Unexpected token `%s`\n", argv[argIdx] ); - return -1; + return CMDLN_PARSER_ERR_FAILED_PARSING; } } @@ -238,8 +270,11 @@ static int16_t parseOpts( { if ( currOpt != NULL ) { - parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); - currOpt->hasBeenParsed = 1; + if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) + { + return CMDLN_PARSER_ERR_FAILED_PARSING; + } + currOpt->hasBeenParsed = true; } currOpt = nextOpt; @@ -253,11 +288,37 @@ static int16_t parseOpts( /* Parse last option */ if ( currOpt != NULL ) { - parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ); - currOpt->hasBeenParsed = 1; + if ( parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct ) != 0 ) + { + return CMDLN_PARSER_ERR_FAILED_PARSING; + } + currOpt->hasBeenParsed = true; } - return 0; + /* 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", opt.props.match ); + if ( opt.props.matchShort != NULL ) + { + fprintf( stderr, "/-%s", opt.props.matchShort ); + } + fprintf( stderr, "\n" ); + + missingMandatory = true; + } + } + if ( missingMandatory ) + { + return CMDLN_PARSER_ERR_FAILED_PARSING; + } + + return CMDLN_PARSER_ERR_OK; } static const char *getBasename( @@ -281,19 +342,22 @@ 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 ); -static void printWhitespace( - const int32_t n ) -{ - for ( int32_t i = 0; i < n; ++i ) + if ( opt.matchShort != NULL ) + { + len += (int32_t) strlen( opt.matchShort ); + } + + if ( opt.placeholder != NULL ) { - fprintf( stderr, " " ); + len += (int32_t) strlen( opt.placeholder ); } + + return len; } static void printOptDescriptionAligned( @@ -317,7 +381,7 @@ static void printOptDescriptionAligned( fprintf( stderr, "%c", *descPtr ); if ( *descPtr == '\n' ) { - printWhitespace( descriptionColumnIdx ); + fprintf( stderr, "%*s", descriptionColumnIdx, "" ); } ++descPtr; } @@ -325,41 +389,114 @@ static void printOptDescriptionAligned( return; } +static void printOptions( + const OptionProps *optionProps, + const int32_t numOptions, + const bool mandatory, + const int32_t maxNumOptChars ) +{ + 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 ) + { + OptionProps opt = optionProps[i]; + + if ( opt.isMandatory != mandatory ) + { + continue; + } + + 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", opt.placeholder ); + numOptChars += 1; + } + + if ( opt.description != NULL ) + { + /* Done printing options column, fill with whitespace until description column */ + fprintf( stderr, "%*s", descriptionColumnIdx - numOptChars, ": " ); + printOptDescriptionAligned( opt.description, descriptionColumnIdx ); + } + else + { + fprintf( stderr, "\n" ); + } + } +} + static void printUsage( const char *argv0, const OptionProps *optionProps, const int32_t numOptions ) { - 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; + int32_t numOptChars; + int32_t maxNumOptChars = 0; + bool hasMandatoryOptions = false; for ( int32_t i = 0; i < numOptions; ++i ) { - const int32_t 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 */ + if ( optionProps[i].isMandatory ) + { + hasMandatoryOptions = true; } } - const int32_t preDescriptionWhitespace = 8; - const int32_t leftColumnAdditionalChars = 7; + fprintf( stderr, "\n" ); + fprintf( stderr, "Usage: %s [options]", getBasename( argv0 ) ); for ( int32_t i = 0; i < numOptions; ++i ) { OptionProps opt = optionProps[i]; - const int32_t optNameLength = totalOptionNameLength( optionProps[i] ); - /* TODO(sgi): make matchShort optional */ - fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort ); + if ( opt.isMandatory ) + { + if ( opt.matchShort != NULL ) + { + fprintf( stderr, " -%s", opt.matchShort ); + } + else + { + fprintf( stderr, " --%s", opt.match ); + } + + if ( opt.placeholder != NULL ) + { + fprintf( stderr, " %s", opt.placeholder ); + } + } + } - printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace ); - printOptDescriptionAligned( opt.description, maxOptNameLength + preDescriptionWhitespace + leftColumnAdditionalChars ); + fprintf( stderr, "\n\n" ); + + if ( hasMandatoryOptions ) + { + fprintf( stderr, "Mandatory parameters:\n---------------------\n" ); + printOptions( optionProps, numOptions, true, maxNumOptChars ); + fprintf( stderr, "\n" ); } + fprintf( stderr, "Options:\n--------\n" ); + printOptions( optionProps, numOptions, false, maxNumOptChars ); + return; } @@ -371,34 +508,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 2627d5566..c6848f79b 100644 --- a/lib_util/cmdln_parser.h +++ b/lib_util/cmdln_parser.h @@ -38,17 +38,21 @@ typedef struct { - int32_t id; - const char *match; - const char *matchShort; - const char *description; + 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. */ + 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 */ -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 ); -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 */ diff --git a/lib_util/split_render_file_read_write.c b/lib_util/split_render_file_read_write.c index 75fd30d88..637560e72 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 ); diff --git a/readme.txt b/readme.txt index f2c21eab2..6a602f135 100644 --- a/readme.txt +++ b/readme.txt @@ -385,16 +385,27 @@ 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 : 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) +-if Format : Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list) +-o File : Path to the output file Options: -------- --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 +-im File : Path to the input metadata file for BINAURAL_SPLIT_PCM input format +-fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM input files +-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 level : Complexity level (1, 2, 3) - will be defined after characterisation +-q : Quiet mode - limit printouts to terminal +-l : List supported audio formats of input file +-fr L : Render frame size in ms L=(5, 10, 20), default is 20 +-s File : Path to the split rendering init params file +-h : Show this help message and exit The usage of the "ambi_converter" program is as follows: -- GitLab From 79f0caccbef7f9b05abc409806bc895441179f87 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 13 Jan 2026 16:36:26 +0100 Subject: [PATCH 2/9] clang format --- apps/isar_post_rend.c | 2 +- lib_dec/lib_dec_fx.c | 2 +- lib_isar/lib_isar_post_rend.h | 2 +- lib_util/cmdl_tools.c | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index e6e45683b..759ababa9 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -1307,7 +1307,7 @@ int main( { for ( i = 0; i < num_subframes; i++ ) { - if( ( error = HeadRotationFileReading( headRotReader, &headRot[i], &Pos[i] ) ) != 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; diff --git a/lib_dec/lib_dec_fx.c b/lib_dec/lib_dec_fx.c index 0787634b1..fcebfad5a 100644 --- a/lib_dec/lib_dec_fx.c +++ b/lib_dec/lib_dec_fx.c @@ -622,7 +622,7 @@ ivas_error IVAS_DEC_Configure( move16(); move16(); - IF ( EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) + IF( EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) || EQ_16( outputConfig, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) { hDecoderConfig->Opt_Headrotation = 1; move16(); diff --git a/lib_isar/lib_isar_post_rend.h b/lib_isar/lib_isar_post_rend.h index 5e5b62082..7dc2a0fe2 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/cmdl_tools.c b/lib_util/cmdl_tools.c index db848c124..00e13b14d 100644 --- a/lib_util/cmdl_tools.c +++ b/lib_util/cmdl_tools.c @@ -228,7 +228,6 @@ bool isEmptyString( } - /*---------------------------------------------------------------------* * strLength() * -- GitLab From 6ccdb87cff5710cc2fee0a618bf2f1d92a23a3f8 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 15 Jan 2026 10:53:20 +0100 Subject: [PATCH 3/9] clang format --- lib_dec/lib_dec_fx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_dec/lib_dec_fx.c b/lib_dec/lib_dec_fx.c index 056250818..c44dfc324 100644 --- a/lib_dec/lib_dec_fx.c +++ b/lib_dec/lib_dec_fx.c @@ -628,7 +628,7 @@ ivas_error IVAS_DEC_Configure( move16(); } - IF( EQ_16(render_num_subframes, IVAS_RENDER_NUM_SUBFR_UNKNOWN) ) + IF( EQ_16( render_num_subframes, IVAS_RENDER_NUM_SUBFR_UNKNOWN ) ) { return IVAS_ERR_WRONG_PARAMS; } -- GitLab From baccb330f029510f009410410acf5498be76f26c Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 15 Jan 2026 10:58:51 +0100 Subject: [PATCH 4/9] fix incorrect inintialization --- apps/decoder.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index d2f26d6dd..a6b05dccc 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -2020,7 +2020,7 @@ static ivas_error decodeG192( bool needNewFrame; int16_t nSamplesRendered, nSamplesRendered_loop, nSamplesToRender; 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_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 } }; IVAS_VECTOR3 Pos[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; SplitFileReadWrite *splitRendWriter = NULL; @@ -2560,13 +2560,15 @@ static ivas_error decodeG192( { if ( headRotReader == NULL ) { - 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; + Quaternions[i].w_fx = -12582912; + Quaternions[i].x_fx = 0; + Quaternions[i].y_fx = 0; + Quaternions[i].z_fx = 0; + Quaternions[i].q_fact = 22; + Pos[i].x = 0.0f; + Pos[i].y = 0.0f; + Pos[i].z = 0.0f; + Pos[i].q_fact = 25; } else { @@ -2809,7 +2811,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_QUATERNION Quaternions[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 }, { -12582912, 0, 0, 0, 22 } }; IVAS_VECTOR3 Pos[IVAS_MAX_PARAM_SPATIAL_SUBFRAMES] = { { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 } }; int16_t vec_pos_update, vec_pos_len; int16_t nOutSamples = 0; -- GitLab From 11c9f236ad878e12e24b123801cac71b6f6fda6d Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 15 Jan 2026 11:07:32 +0100 Subject: [PATCH 5/9] fix incorrect inintialization --- apps/isar_post_rend.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index 196930428..52abe4fe7 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -1318,13 +1318,14 @@ int main( { 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; + headRot[i].w_fx = -12582912; + headRot[i].x_fx = 0; + headRot[i].y_fx = 0; + headRot[i].z_fx = 0; + headRot[i].q_fact = 22; + Pos[i].x_fx = 0; + Pos[i].y_fx = 0; + Pos[i].z_fx = 0; } } -- GitLab From ff351d0a8c2edbb851878effaffa5ab2b7144c80 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 20 Jan 2026 13:09:13 +0100 Subject: [PATCH 6/9] do not call IVAS_DEC_GetRenderNumSubfr() twice --- apps/decoder.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 399aadb44..e601d1e1a 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -417,22 +417,10 @@ int main( goto cleanup; } - if ( ( error = IVAS_DEC_GetRenderNumSubfr( hIvasDec, &arg.render_num_subframes ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nIVAS_DEC_GetRenderNumSubfr failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); - goto cleanup; - } - - if ( arg.render_num_subframes != asked_num_subframes ) - { - fprintf( stderr, "\nChanged render framesize, only 20ms are allowed for decoding to EXT!\n" ); - } - /*------------------------------------------------------------------------------------------* * Configure Split rendering *------------------------------------------------------------------------------------------*/ - asked_num_subframes = arg.render_num_subframes; if ( arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg.outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) { if ( ( error = IVAS_DEC_EnableSplitRendering( hIvasDec ) ) != IVAS_ERR_OK ) @@ -440,20 +428,20 @@ int main( fprintf( stderr, "\nSplit rendering configure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; } + } - if ( ( error = IVAS_DEC_GetRenderNumSubfr( hIvasDec, &arg.render_num_subframes ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nIVAS_DEC_GetRenderNumSubfr failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); - goto cleanup; - } + if ( ( error = IVAS_DEC_GetRenderNumSubfr( hIvasDec, &arg.render_num_subframes ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nIVAS_DEC_GetRenderNumSubfr failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); + goto cleanup; + } #ifdef FIX_1449_RENDERER_FRAME_SIZE_UNCLEAR_IN_ISAR - if ( !arg.renderConfigEnabled && ( arg.render_num_subframes != asked_num_subframes ) ) - { - fprintf( stderr, "\nChanged render framesize, only 20ms are allowed for non-0dof split rendering!\n" ); - } -#endif + if ( !arg.renderConfigEnabled && ( arg.render_num_subframes != asked_num_subframes ) ) + { + fprintf( stderr, "\nChanged render framesize, only 20ms allowed!\n" ); } +#endif /*------------------------------------------------------------------------------------------* * Configure VoIP mode @@ -3942,6 +3930,15 @@ static ivas_error restartDecoder( goto cleanup; } + if ( arg->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) + { + if ( ( error = IVAS_DEC_EnableSplitRendering( hIvasDec ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nSplit rendering configure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); + goto cleanup; + } + } + if ( ( error = IVAS_DEC_GetRenderNumSubfr( hIvasDec, &arg->render_num_subframes ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nIVAS_DEC_GetRenderNumSubfr failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); -- GitLab From d5d4ccd5c39625391e45096a34275f204246b03b Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 20 Jan 2026 13:11:47 +0100 Subject: [PATCH 7/9] remove complexity level from ISAR post-renderer --- apps/isar_post_rend.c | 32 -------------------------------- lib_isar/lib_isar_post_rend.h | 8 -------- 2 files changed, 40 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index ba0001541..cd10d9d37 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -109,7 +109,6 @@ typedef struct 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; bool delayCompensationEnabled; bool quietModeEnabled; IVAS_RENDER_NUM_SUBFR render_num_subframes; @@ -122,7 +121,6 @@ typedef enum CmdLnOptionId_outputFile, CmdLnOptionId_sampleRate, CmdLnOptionId_trajFile, - CmdLnOptionId_complexityLevel, CmdLnOptionId_noDelayCmp, CmdLnOptionId_quietModeEnabled, CmdLnOptionId_inputMetadata, @@ -192,13 +190,6 @@ static const CmdLnParser_Option cliOptions[] = { .matchShort = "no_delay_cmp", .description = "Turn off delay compensation", }, - { - .id = CmdLnOptionId_complexityLevel, - .match = "complexity_level", - .matchShort = "level", - .placeholder = "", - .description = "Complexity level (1, 2, 3) - will be defined after characterisation", - }, { .id = CmdLnOptionId_quietModeEnabled, .match = "quiet", @@ -432,7 +423,6 @@ static CmdlnArgs defaultArgs( clearString( args.headRotationFilePath ); clearString( args.splitRendBFIFilePath ); - args.complexityLevel = ISAR_POST_REND_COMPLEXITY_LEVEL_THREE; args.delayCompensationEnabled = true; args.quietModeEnabled = false; @@ -546,28 +536,6 @@ static int16_t parseOption( } strncpy( args->splitRendBFIFilePath, optionValues[0], POST_REND_MAX_CLI_ARG_LENGTH - 1 ); break; - case CmdLnOptionId_complexityLevel: - 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 ) - { - 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( stderr, "Complexity levels 1 and 2 will be defined after characterisation - default to level 3 (full functionality).\n" ); - } - break; case CmdLnOptionId_noDelayCmp: if ( numOptionValues != 0 ) { diff --git a/lib_isar/lib_isar_post_rend.h b/lib_isar/lib_isar_post_rend.h index c7d4c2141..d7f1cce84 100644 --- a/lib_isar/lib_isar_post_rend.h +++ b/lib_isar/lib_isar_post_rend.h @@ -82,14 +82,6 @@ typedef struct ISAR_POST_REND const *ISAR_POST_REND_CONST_HANDLE; typedef UWord16 ISAR_POST_REND_InputId; -typedef enum _ISAR_POST_REND_COMPLEXITY_LEVEL -{ - ISAR_POST_REND_COMPLEXITY_LEVEL_ONE = 1, - ISAR_POST_REND_COMPLEXITY_LEVEL_TWO = 2, - ISAR_POST_REND_COMPLEXITY_LEVEL_THREE = 3 -} ISAR_POST_REND_COMPLEXITY_LEVEL; - - /* clang-format off */ /*----------------------------------------------------------------------------------* * ISAR post-renderer function prototypes -- GitLab From b0db2d05b0980c1ab8efa550fa0e045690e8e881 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Tue, 20 Jan 2026 13:14:07 +0100 Subject: [PATCH 8/9] modifications to cmd-line parameter descriptions --- apps/isar_post_rend.c | 8 ++++---- readme.txt | 23 +++++++++++------------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/apps/isar_post_rend.c b/apps/isar_post_rend.c index cd10d9d37..c3fe556c8 100644 --- a/apps/isar_post_rend.c +++ b/apps/isar_post_rend.c @@ -145,7 +145,7 @@ static const CmdLnParser_Option cliOptions[] = { .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)", + .description = "Audio format of input file (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM or RTPDUMP)", .isMandatory = true, }, { @@ -161,14 +161,14 @@ static const CmdLnParser_Option cliOptions[] = { .match = "input_metadata", .matchShort = "im", .placeholder = "", - .description = "Path to the input metadata file for BINAURAL_SPLIT_PCM input format", + .description = "Path to the input metadata file (mandatory 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 input files", + .description = "Input sampling rate in kHz (16, 32, 48) (mandatory for raw PCM input files)", }, { .id = CmdLnOptionId_trajFile, @@ -214,7 +214,7 @@ static const CmdLnParser_Option cliOptions[] = { .match = "sr_params", .matchShort = "s", .placeholder = "", - .description = "Path to the split rendering init params file", + .description = "Path to the split rendering SDP init params file (mandatory for RTPDUMP input format)", }, { .id = CmdLnOptionId_help, diff --git a/readme.txt b/readme.txt index a94133f20..0f4848d61 100644 --- a/readme.txt +++ b/readme.txt @@ -387,24 +387,23 @@ The usage of the "ISAR_post_rend" program is as follows: Usage: ISAR_post_rend [options] -i -if -o -Mandatory parameters: ---------------------- --i File : 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) --if Format : Audio format of input file (e.g. BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM, ... Use -l for a list) --o File : Path to the output file - -Options: --------- --im File : Path to the input metadata file for BINAURAL_SPLIT_PCM input format --fs : Input sampling rate in kHz (16, 32, 48) - required only with raw PCM input files +Mandatory parameters: +--------------------- +-i File : 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) +-if Format : Audio format of input file (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM or RTPDUMP) +-o File : Path to the output file + +Options : +------- - +-im File : Path to the input metadata file (mandatory for BINAURAL_SPLIT_PCM input format) +-fs : Input sampling rate in kHz (16, 32, 48) (mandatory for raw PCM input files) -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 level : Complexity level (1, 2, 3) - will be defined after characterisation -q : Quiet mode - limit printouts to terminal -l : List supported audio formats of input file -fr L : Render frame size in ms L=(5, 10, 20), default is 20 --s File : Path to the split rendering init params file +-s File : Path to the split rendering SDP init params file (mandatory for RTPDUMP input format) -h : Show this help message and exit -- GitLab From 739f8ae488e78cfb97c7107fbea42708969f452f Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 21 Jan 2026 09:30:13 +0100 Subject: [PATCH 9/9] slight modification to the wording describing ISAR output formats --- lib_rend/ivas_output_init_fx.c | 4 ++-- readme.txt | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib_rend/ivas_output_init_fx.c b/lib_rend/ivas_output_init_fx.c index aa3bed7b9..ec279e168 100644 --- a/lib_rend/ivas_output_init_fx.c +++ b/lib_rend/ivas_output_init_fx.c @@ -615,11 +615,11 @@ ivas_error get_channel_config( } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED ) ) { - strcpy( (char *) str, "Binaural: split-rendering mode using encoded data" ); + strcpy( (char *) str, "Binaural: split-rendering mode with encoded output in an intermediate format" ); } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM ) ) { - strcpy( (char *) str, "Binaural: split-rendering mode using PCM data" ); + strcpy( (char *) str, "Binaural: split-rendering mode with separate PCM binaural output and metadata bitstream output" ); } ELSE IF( EQ_16( (Word16) config, IVAS_AUDIO_CONFIG_EXTERNAL ) ) { diff --git a/readme.txt b/readme.txt index 0f4848d61..2eb1308b0 100644 --- a/readme.txt +++ b/readme.txt @@ -389,21 +389,21 @@ Usage: ISAR_post_rend [options] -i -if -o Mandatory parameters: --------------------- --i File : 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) --if Format : Audio format of input file (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM or RTPDUMP) --o File : Path to the output file +-i File : Path to the input File (WAV or raw PCM for BINAURAL_SPLIT_PCM format, or encoded intermediate-format data for BINAURAL_SPLIT_CODED format) +-if Format : Audio format of input File (BINAURAL_SPLIT_CODED, BINAURAL_SPLIT_PCM or RTPDUMP) +-o File : Path to the output File Options : ------- - --im File : Path to the input metadata file (mandatory for BINAURAL_SPLIT_PCM input format) --fs : Input sampling rate in kHz (16, 32, 48) (mandatory for raw PCM input files) --T File : Head rotation trajectory file for simulation of head tracking --prbfi File : Split rendering BFI (Bad Frame Indicator) file +-im File : Path to the coded metadata File (mandatory for BINAURAL_SPLIT_PCM format) +-fs : Input sampling rate in kHz (16, 32, 48) (mandatory for raw PCM input Files) +-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 -q : Quiet mode - limit printouts to terminal --l : List supported audio formats of input file +-l : List supported audio formats of input File -fr L : Render frame size in ms L=(5, 10, 20), default is 20 --s File : Path to the split rendering SDP init params file (mandatory for RTPDUMP input format) +-s File : Path to the split rendering SDP init params File (mandatory for RTPDUMP format) -h : Show this help message and exit -- GitLab