diff --git a/apps/decoder.c b/apps/decoder.c index 10a8ce23705d30e0326c0b008a69db37a7d3ed35..cff06f1a9ccd03b291dfd462450fa52d3f3cd969 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -199,9 +199,15 @@ int main( IVAS_DEC_HRTF_HANDLE *hHrtfTD = NULL; IVAS_DEC_HRTF_CREND_HANDLE *hSetOfHRTF = NULL; IVAS_DEC_HRTF_STATISTICS_HANDLE *hHrtfStatistics = NULL; +#ifdef LIMITER_CHECK + int32_t cnt_frames_limited; + float ratio_frames_limited; + float avg_limiter_gain; + float avg_limiter_gain_in_limited_frames; + float max_limiter_gain; +#endif #ifdef DEBUGGING int32_t noClipping; - int32_t cnt_frames_limited; #ifdef DEBUG_SBA_AUDIO_DUMP int16_t numOutChannels, numTransportChannels, pca_ingest_channels; #endif @@ -887,11 +893,19 @@ int main( goto cleanup; } -#ifdef DEBUGGING - if ( ( cnt_frames_limited = IVAS_DEC_GetCntFramesLimited( hIvasDec ) ) > 0 ) +#ifdef LIMITER_CHECK + if ( ( cnt_frames_limited = IVAS_DEC_GetCntFramesLimited( hIvasDec ) ) >= 0 ) { - fprintf( stdout, "Limiter applied in %d frames.\n\n", cnt_frames_limited ); + fprintf( stdout, "Total frames with Limiter application: %d\n\n", cnt_frames_limited ); + ratio_frames_limited = IVAS_DEC_GetRatioFramesLimited( hIvasDec ); + fprintf( stdout, "Ratio of frames with limiting: %0.2f\n\n", ratio_frames_limited); + IVAS_DEC_GetLimiterGainInfo( hIvasDec, &avg_limiter_gain, &max_limiter_gain, &avg_limiter_gain_in_limited_frames ); + fprintf( stdout, "Average Limiter Gain (dB): %0.2f\n", avg_limiter_gain); + fprintf( stdout, "Average Limiter Gain (limited frames only) (dB): %0.2f\n", avg_limiter_gain_in_limited_frames); + fprintf( stdout, "Max Limiter Gain (dB): %0.2f\n", max_limiter_gain); } +#endif +#ifdef DEBUGGING if ( ( noClipping = IVAS_DEC_GetNoCLipping( hIvasDec ) ) > 0 ) { fprintf( stdout, "Clipping (saturation) detected: %d samples clipped!!!\n\n", noClipping ); diff --git a/lib_com/options.h b/lib_com/options.h index f79b3468396f65f777e1dc2fd031e0ecab217607..2f27b1fd8d3d934e2570b09a13bf9001dabb5894 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -154,6 +154,7 @@ /*#define FIX_I4_OL_PITCH*/ /* fix open-loop pitch used for EVS core switching */ /*#define SPLIT_REND_WITH_HEAD_ROT */ /* Dlb,FhG: Split Rendering contributions 21 and 35 */ +#define LIMITER_CHECK #define FIX_777_COMBI_RENDER_CONFIG_FILE /* Philips: Fix for combined renderer config file support */ #define FIX_966_VAR_OVERFLOW_IN_HARM_MODEL_ARI /* FhG: fix and undef behaviour bug in the harmonic TCX model arithmetic coder */ diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 61d452d9da43cdf0a81ef57b940755867552fd23..7c4380e49a7bfda3b515c18c0185ddd3b6e22b04 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3518,61 +3518,111 @@ static ivas_error evs_dec_main( return IVAS_ERR_OK; } +#ifdef LIMITER_CHECK -#ifdef DEBUGGING /*---------------------------------------------------------------------* - * IVAS_DEC_GetBer_detect_flag() + * IVAS_DEC_GetCntFramesLimited() * - * return BER_detect flag + * return number of frames where limiter is applied *---------------------------------------------------------------------*/ -bool IVAS_DEC_GetBerDetectFlag( +int32_t IVAS_DEC_GetCntFramesLimited( IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ ) { - if ( hIvasDec->st_ivas->BER_detect == 1 ) + if ( hIvasDec->st_ivas->hLimiter == NULL ) { - return 1; + return 0; } else { - return 0; + return hIvasDec->st_ivas->hLimiter->cnt_frames_limited; } } - /*---------------------------------------------------------------------* - * IVAS_DEC_GetNoCLipping() + * IVAS_DEC_GetRatioFramesLimited() * - * return number of clipped samples + * return number of frames where limiter is applied *---------------------------------------------------------------------*/ -int32_t IVAS_DEC_GetNoCLipping( +float IVAS_DEC_GetRatioFramesLimited( IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ ) { - return hIvasDec->st_ivas->noClipping; + if ( hIvasDec->st_ivas->hLimiter == NULL ) + { + return 0; + } + else + { + return hIvasDec->st_ivas->hLimiter->frames_limited_avg; + } } - /*---------------------------------------------------------------------* - * IVAS_DEC_GetCntFramesLimited() + * IVAS_DEC_GetRatioFramesLimited() * * return number of frames where limiter is applied *---------------------------------------------------------------------*/ -int32_t IVAS_DEC_GetCntFramesLimited( - IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ +void IVAS_DEC_GetLimiterGainInfo( + IVAS_DEC_HANDLE hIvasDec, /* i : IVAS decoder handle */ + float *gain_avg, + float *gain_max, + float *gain_avg_in_limited_frames ) { if ( hIvasDec->st_ivas->hLimiter == NULL ) { - return 0; + *gain_avg = 0.0f; + *gain_max = 0.0f; + *gain_avg_in_limited_frames = 0.0f; } else { - return hIvasDec->st_ivas->hLimiter->cnt_frames_limited; + *gain_avg = hIvasDec->st_ivas->hLimiter->gain_avg; + *gain_max = 20*log10f(1.0f/(hIvasDec->st_ivas->hLimiter->gain_max + EPSILON)); + *gain_avg_in_limited_frames = hIvasDec->st_ivas->hLimiter->gain_avg_in_limited_frames; + } + return; +} +#endif + + +#ifdef DEBUGGING +/*---------------------------------------------------------------------* + * IVAS_DEC_GetBer_detect_flag() + * + * return BER_detect flag + *---------------------------------------------------------------------*/ + +bool IVAS_DEC_GetBerDetectFlag( + IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ +) +{ + if ( hIvasDec->st_ivas->BER_detect == 1 ) + { + return 1; } + else + { + return 0; + } +} + + +/*---------------------------------------------------------------------* + * IVAS_DEC_GetNoCLipping() + * + * return number of clipped samples + *---------------------------------------------------------------------*/ + +int32_t IVAS_DEC_GetNoCLipping( + IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ +) +{ + return hIvasDec->st_ivas->noClipping; } diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 2dccb455256a25ef6f071d0a99d306deac1ba0c7..430c09060fe2c9439c5f0b2fb906cbaecacc3ccb 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -342,16 +342,29 @@ ivas_error IVAS_DEC_GetRenderFramesizeMs( uint32_t *render_framesize /* o : render framesize in samples */ ); -#ifdef DEBUGGING -bool IVAS_DEC_GetBerDetectFlag( +#ifdef LIMITER_CHECK +int32_t IVAS_DEC_GetCntFramesLimited( IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ ); -int32_t IVAS_DEC_GetNoCLipping( +float IVAS_DEC_GetRatioFramesLimited( + IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ +); + +void IVAS_DEC_GetLimiterGainInfo( + IVAS_DEC_HANDLE hIvasDec, /* i : IVAS decoder handle */ + float *gain_avg, + float *gain_max, + float *gain_avg_in_limited_frames +); +#endif + +#ifdef DEBUGGING +bool IVAS_DEC_GetBerDetectFlag( IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ ); -int32_t IVAS_DEC_GetCntFramesLimited( +int32_t IVAS_DEC_GetNoCLipping( IVAS_DEC_HANDLE hIvasDec /* i : IVAS decoder handle */ ); diff --git a/lib_rend/ivas_limiter.c b/lib_rend/ivas_limiter.c index da214e568a6c32d02ef624aa517e1bc97c0b9df7..5f9a97dc8fc013fd98971d9d4272b47fea2ad3ec 100644 --- a/lib_rend/ivas_limiter.c +++ b/lib_rend/ivas_limiter.c @@ -132,8 +132,13 @@ ivas_error ivas_limiter_open( hLimiter->release_heuristic = 0.f; hLimiter->attack_constant = powf( 0.01f, 1.0f / ( IVAS_LIMITER_ATTACK_SECONDS * sampling_rate ) ); hLimiter->strong_saturation_count = 0; -#ifdef DEBUGGING +#ifdef LIMITER_CHECK hLimiter->cnt_frames_limited = 0; + hLimiter->cnt_frames = 0; + hLimiter->gain_avg = 0.0f; + hLimiter->frames_limited_avg = 0.0f; + hLimiter->gain_avg_in_limited_frames = 0.0f; + hLimiter->gain_max=1.0f; #endif for ( i = 0; i < max_num_channels; ++i ) @@ -294,7 +299,7 @@ void limiter_process( * ^ * React faster when release time should be increased */ -#ifdef DEBUGGING +#ifdef LIMITER_CHECK if ( max_val > threshold ) { hLimiter->cnt_frames_limited++; @@ -379,6 +384,19 @@ void limiter_process( /* Save last gain and release heuristic values for next frame */ hLimiter->gain = gain; hLimiter->release_heuristic = releaseHeuristic; +#ifdef LIMITER_CHECK + hLimiter->cnt_frames++; + hLimiter->frames_limited_avg = (float)hLimiter->cnt_frames_limited/(float)hLimiter->cnt_frames; + hLimiter->gain_avg = hLimiter->gain_avg + (20*log10f(1.0f/(hLimiter->gain + EPSILON))-hLimiter->gain_avg)/(float)hLimiter->cnt_frames; + if ( max_val > threshold ) + { + hLimiter->gain_avg_in_limited_frames = hLimiter->gain_avg_in_limited_frames + (20*log10f(1.0f/(hLimiter->gain + EPSILON))-hLimiter->gain_avg_in_limited_frames)/(float)hLimiter->cnt_frames_limited; + } + if ( hLimiter->gain_max > hLimiter->gain ) + { + hLimiter->gain_max = hLimiter->gain; + } +#endif return; } diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index d5701c81f630b77b63b821f68765e1f3a2e200ff..e5ae179da1573f1841574ec2350d942c2dc970e4 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -1560,8 +1560,13 @@ typedef struct float release_heuristic; float attack_constant; int16_t strong_saturation_count; -#ifdef DEBUGGING +#ifdef LIMITER_CHECK int32_t cnt_frames_limited; /* counter of frames in which the limiter is applied */ + int32_t cnt_frames; + float frames_limited_avg; + float gain_avg; + float gain_avg_in_limited_frames; + float gain_max; #endif } IVAS_LIMITER, *IVAS_LIMITER_HANDLE; diff --git a/scripts/IvasBuildAndRunChecks.py b/scripts/IvasBuildAndRunChecks.py index 4bca48a672fd4b3febddc6c995d5b8c7d80e72dc..f8d90e46828fab19ab84f3fef40979b133f03f71 100755 --- a/scripts/IvasBuildAndRunChecks.py +++ b/scripts/IvasBuildAndRunChecks.py @@ -51,7 +51,7 @@ class IvasBuildAndRunChecks(IvasScriptsCommon.IvasScript): nargs="*", default="all", help="List of checks to run, default all", - choices=["all", "CLANG1", "CLANG2", "CLANG3", "VALGRIND", "COMPLEXITY"], + choices=["all", "CLANG1", "CLANG2", "CLANG3", "VALGRIND", "COMPLEXITY", "LIMITER"], ) self.parser.add_argument( "--srcdir", @@ -95,6 +95,11 @@ class IvasBuildAndRunChecks(IvasScriptsCommon.IvasScript): help="suppression file for undef behaviour sanitizer", default=None, ) + self.parser.add_argument( + "--create_limiter_info", + help="create limiter info with the given prefix", + default="", + ) def run(self): @@ -207,6 +212,20 @@ class IvasBuildAndRunChecks(IvasScriptsCommon.IvasScript): self.args["create_complexity_tables"] ) + if "LIMITER" in checks and self.args["create_limiter_info"]: + cmd = ["git", "rev-parse", "HEAD"] + commit_hash = subprocess.run(cmd, capture_output=True).stdout.decode("utf8") + limiter_analyzer = br.build_and_run_dict["LIMITER"]["analyzer"] + if self.args["formats"]: + limiter_analyzer.set_format_select_list(self.args["formats"]) + if self.args["oc_dict"]: + for ivas_format in self.args["formats"]: + limiter_analyzer.set_format_output_config( + ivas_format, self.args["oc_dict"] + ) + limiter_analyzer.ls_modes() + limiter_analyzer.write_limiter_html_file(self.args["create_limiter_info"], commit_hash) + returncode = 0 for check, ret_val in zip(checks, checks_ret_val): runner = br.build_and_run_dict[check]["runner"] diff --git a/scripts/pyivastest/IvasModeAnalyzer.py b/scripts/pyivastest/IvasModeAnalyzer.py index 8d1dd2ddf082e51435115fe8a54d9aa98caeb804..f3c3e1b613a3ce81b16a3ec25cbb654bc5c6f32b 100644 --- a/scripts/pyivastest/IvasModeAnalyzer.py +++ b/scripts/pyivastest/IvasModeAnalyzer.py @@ -41,6 +41,49 @@ from pyivastest.constants import LOG_FILE_EXT from pyivastest.IvasBaseClass import IvasBaseClass from pyivastest.IvasModeCollector import IvasModeCollector +LIMITER_RESULTS = { + "ABSLIMITEDFRAMES": { + "keyword": "Total frames with Limiter application:", + "number_format": "{:.0f}", + "position": 0, + "keyword_suffix": False, + "strip_suffix": False, + "fail_threshold": 1000000000 + }, + "RATIOLIMITEDFRAMES": { + "keyword": "Ratio of frames with limiting:", + "number_format": "{:.5g}", + "position": 0, + "keyword_suffix": False, + "strip_suffix": False, + "fail_threshold": 0.05 + }, + "AVGLIMITERGAIN": { + "keyword": "Average Limiter Gain (dB):", + "number_format": "{:.5g}", + "position": 0, + "keyword_suffix": False, + "strip_suffix": False, + "fail_threshold": 1 + }, + "AVGLIMITERGAINLIMITEDFRAMES": { + "keyword": "Average Limiter Gain (limited frames only) (dB):", + "number_format": "{:.5g}", + "position": 0, + "keyword_suffix": False, + "strip_suffix": False, + "fail_threshold": 2 + }, + "MAXLIMITERGAIN": { + "keyword": "Max Limiter Gain (dB):", + "number_format": "{:.5g}", + "position": 0, + "max_or_add": "max", + "keyword_suffix": False, + "strip_suffix": False, + "fail_threshold": 5 + }, +} INSTRUMENTED_RESULTS = { "WMOPS": { "keyword": "total", @@ -120,6 +163,15 @@ body { table, th, td { border: 1px solid black; } +td.pass { + background-color: green; + text-color:white; +} +td.fail { + background-color: red; + text-color: white; + font-weight: bold; +} table { width: 80%; border-collapse: collapse; @@ -137,6 +189,11 @@ h1 { margin-top: 1em; margin-bottom: 1em; } +h2 { + text-align: center; + margin-top: 1em; + margin-bottom: 1em; +} """ @@ -176,6 +233,8 @@ class IvasModeAnalyzer(IvasModeCollector): self.analyze_encoder = True self.analyze_decoder = True self.html_table = None + self.encoder_cmdline_options = None + self.decoder_cmdline_options = None def ls_modes(self): """ """ @@ -221,7 +280,10 @@ class IvasModeAnalyzer(IvasModeCollector): oc = os.path.splitext(f)[1][1:] f = os.path.splitext(os.path.splitext(f)[0])[0] item, suffices = f.split("".join(["_", mode])) - if item not in self.available_logs[mode]["items"]: + if ( + item not in self.available_logs[mode]["items"] + and oc in self.flat_mode_list[mode]['cmd']['dec'] + ): self.available_logs[mode]["items"].update( {item: {"enc": False, "dec": [oc]}} ) @@ -231,11 +293,15 @@ class IvasModeAnalyzer(IvasModeCollector): oc not in self.available_logs[mode]["items"][item]["dec"] and oc != "proc" + and oc in self.flat_mode_list[mode]['cmd']['dec'] ): self.available_logs[mode]["items"][item]["dec"].append( oc ) - if oc not in self.available_logs[mode]["oc_list"]: + if ( + oc not in self.available_logs[mode]["oc_list"] + and oc in self.flat_mode_list[mode]['cmd']['dec'] + ): self.available_logs[mode]["oc_list"].append(oc) @staticmethod @@ -556,6 +622,47 @@ class IvasModeAnalyzer(IvasModeCollector): result_table.append(result_line) return result_table + def get_limiter_table( + self + ): + """ + + Parameters + ---------- + + Returns + ------- + + """ + self.select_logs() + sorted_modes = self.sort_log_modes() + + # build first line of the table + result_table = [["mode", "item"]] + for lim_result in LIMITER_RESULTS.keys(): + result_table[0].append(lim_result) + + for mode in sorted_modes: # sorted(self.selected_logs.keys()): + formatted_name = self.selected_logs[mode]["table_name"] + # enc or common only, we do not collect dec values... + + for oc in self.selected_logs[mode]["oc_list"]: + formatted_name_oc = formatted_name + " to " + oc + for item in self.selected_logs[mode]["items"]: + result_line = [formatted_name_oc, item] + if oc in self.selected_logs[mode]["items"][item]["dec"]: + dec_log_name = self.get_dec_log_file_name(item, mode, oc) + for lim_result in LIMITER_RESULTS.keys(): + result_line.append( + self.get_log_value_from_file( + dec_log_name, + LIMITER_RESULTS[lim_result]['keyword'], + LIMITER_RESULTS[lim_result]['position'], + strip_suffix=LIMITER_RESULTS[lim_result]['strip_suffix'], + )) + result_table.append(result_line) + return result_table + @staticmethod def parse_valgrind_log(filename): """ @@ -1049,6 +1156,113 @@ class IvasModeAnalyzer(IvasModeCollector): for line in page: f.write(line) + def write_limiter_html_file(self, basefilename, revision): + + # create header for table + hdr_tmpl = "