From 306455c33366d872af3fcb5fe89947945cb2b167 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 16 Feb 2026 11:27:22 +0100 Subject: [PATCH 1/4] add mld_playback_level argument --- scripts/pyaudio3dtools/audioarray.py | 5 ++++- tests/cmp_pcm.py | 8 +++++++ .../test_param_file.py | 9 ++++---- tests/codec_be_on_mr_nonselection/test_sba.py | 4 ++-- tests/conftest.py | 11 ++++++++++ tests/renderer/utils.py | 22 ++++++++++--------- tests/renderer_short/utils.py | 15 ++++++++----- tests/split_rendering/utils.py | 8 +++++-- 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/scripts/pyaudio3dtools/audioarray.py b/scripts/pyaudio3dtools/audioarray.py index 7793e978c1..e1d3c62d7f 100644 --- a/scripts/pyaudio3dtools/audioarray.py +++ b/scripts/pyaudio3dtools/audioarray.py @@ -248,7 +248,8 @@ def compare( test_start_offset_ms: int = 0, ref_jbm_tf: Optional[Union[Path, str]] = None, test_jbm_tf: Optional[Union[Path, str]] = None, - handle_differing_lengths: HandleDifferingLengths = "fail", + handle_differing_lengths: HandleDifferingLengths = HandleDifferingLengths.FAIL, + mld_playback_level: float = 92.0, ) -> dict: """Compare two audio arrays @@ -390,6 +391,8 @@ def compare( # wav-diff return code is 1 if differences are found which # would cause parse_wav_diff to raise an Exception on these cases "--no-fail", + "--mld-playback-level", + f"{mld_playback_level}", str(tmpfile_ref), str(tmpfile_test), ] diff --git a/tests/cmp_pcm.py b/tests/cmp_pcm.py index a646e20ae5..e902d665f1 100755 --- a/tests/cmp_pcm.py +++ b/tests/cmp_pcm.py @@ -42,6 +42,7 @@ def cmp_pcm( cut_jbm_tf: Optional[Path] = None, quiet: Optional[bool] = False, scalefac: int = 1, + mld_playback_level: float = 92.0, split_idx: np.ndarray = np.empty(0), ) -> tuple[List[int], List[str]]: """ @@ -111,6 +112,7 @@ def cmp_pcm( ref_jbm_tf=ref_jbm_tf, test_jbm_tf=cut_jbm_tf, handle_differing_lengths=handle_differing_lengths, + mld_playback_level=mld_playback_level, ) output_differs = 0 @@ -277,6 +279,12 @@ if __name__ == "__main__": dest="scalefac", help="Scale factor to be applied before comparing the output. Useful when input scaling has been applied.", ) + parser.add_argument( + "--mld-playback-level", + type=float, + help="Playback level arg for MLD in wav-diff", + default=92.0, + ) parser.add_argument("--quiet", action="store_true") args = vars(parser.parse_args()) diff --git a/tests/codec_be_on_mr_nonselection/test_param_file.py b/tests/codec_be_on_mr_nonselection/test_param_file.py index f456a3d860..18e9f0a71c 100644 --- a/tests/codec_be_on_mr_nonselection/test_param_file.py +++ b/tests/codec_be_on_mr_nonselection/test_param_file.py @@ -639,7 +639,7 @@ def run_test( odg_ref=odg_ref, ref_jbm_tf=ref_tracefile_dec, cut_jbm_tf=dut_tracefile_dec, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) @@ -669,7 +669,7 @@ def run_test( odg_ref=odg_ref, ref_jbm_tf=ref_tracefile_dec, cut_jbm_tf=dut_tracefile_dec, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) output_differs_parts += output_differs_splits @@ -725,9 +725,8 @@ def run_test( if this_meta_differs == False: if dut_metadata_file.suffix == ".met": this_meta_differs = check_masa_meta_diff( - ref=ref_metadata_file, - dut=dut_metadata_file, - print_stdout=True) + ref=ref_metadata_file, dut=dut_metadata_file, print_stdout=True + ) else: if not filecmp.cmp(dut_metadata_file, ref_metadata_file): print("Output metadata differs for file: " + md_file) diff --git a/tests/codec_be_on_mr_nonselection/test_sba.py b/tests/codec_be_on_mr_nonselection/test_sba.py index b6dd2b75a6..f6b9be6e3f 100644 --- a/tests/codec_be_on_mr_nonselection/test_sba.py +++ b/tests/codec_be_on_mr_nonselection/test_sba.py @@ -1348,7 +1348,7 @@ def sba_dec( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) @@ -1376,7 +1376,7 @@ def sba_dec( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) output_differs_parts += output_differs_splits diff --git a/tests/conftest.py b/tests/conftest.py index 7bf690151d..f06fe3ce62 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -317,6 +317,14 @@ def pytest_addoption(parser): default=1, ) + parser.addoption( + "--mld-playback-level", + action="store", + help="Playback level arg for MLD calulcation.", + type=float, + default=92.0, + ) + parser.addoption( "--split-comparison", action="store_true", @@ -449,6 +457,7 @@ def test_info(request): if hasattr(request, "error"): pytest.fail(request.error) + @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): # Use hook to catch exceptions @@ -464,6 +473,7 @@ def pytest_runtest_makereport(item, call): # Capture exception in test_info test_info.error = str(call.excinfo.value) + @pytest.fixture(scope="session") def split_comparison(request): return request.config.option.split_comparison @@ -1265,6 +1275,7 @@ def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: in_fs=fs * 1000, ) + # TODO: needs mld-playback-level dmx_differs, reason = cmp_pcm( dmx_file_ref_tmp.name, dmx_file_dut_tmp.name, diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py index 0303b897ca..1e0b7c3e03 100644 --- a/tests/renderer/utils.py +++ b/tests/renderer/utils.py @@ -69,7 +69,8 @@ from pyaudio3dtools.audiofile import readfile from ..cmp_pcm import cmp_pcm from ..conftest import get_split_idx, parse_properties -def _run_cmd(cmd, test_info=None, env=None ): + +def _run_cmd(cmd, test_info=None, env=None): """ Helper function for running some command. Raises a SystemError if either the return code is non-zero or a USAN printout is detected @@ -108,6 +109,7 @@ def run_ivas_isar_enc_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning IVAS ISAR encoder command\n{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) + def run_ivas_isar_dec_cmd(cmd, test_info=None, env=None): if BIN_SUFFIX_MERGETARGET in cmd[0]: logging.info(f"\nREF decoder command:\n\t{' '.join(cmd)}\n") @@ -115,6 +117,7 @@ def run_ivas_isar_dec_cmd(cmd, test_info=None, env=None): logging.info(f"\nDUT decoder command:\n\t{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) + def run_isar_post_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR post renderer command\n{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) @@ -428,7 +431,7 @@ def run_renderer( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) @@ -455,7 +458,7 @@ def run_renderer( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) output_differs_parts += output_differs_splits @@ -484,10 +487,9 @@ def run_renderer( meta_file_ref = out_file_ref + ".met" meta_file_cut = out_file + ".met" - if check_masa_meta_diff( - ref=meta_file_ref, - dut=meta_file_cut, - print_stdout=True): + if check_masa_meta_diff( + ref=meta_file_ref, dut=meta_file_cut, print_stdout=True + ): pytest.fail("Metadata file differs from reference") return out_file @@ -777,19 +779,19 @@ def scene_description_file(in_fmt, metadata_tmp, n_obj, input_file, in_meta_file metadata_tmp ).parent # File names must be relative to config file location fp_meta.write(f"{os.path.relpath(input_file, currdir)}\n") # Input file - fp_meta.write(f"{n_obj+1}\n") # Number of sources + fp_meta.write(f"{n_obj + 1}\n") # Number of sources for n in range(0, n_obj): if in_meta_files[n] == "NULL": md_file = "1\n1,0,0" # NULL metadata position: azim=0,elev=0 for 1 frame, looped throughout all frames. else: md_file = os.path.relpath(in_meta_files[n], currdir) - fp_meta.write(f"ISM\n{n+1}\n{md_file}\n") # ISM metadata + fp_meta.write(f"ISM\n{n + 1}\n{md_file}\n") # ISM metadata if "OSBA" in in_fmt: fp_meta.write( "gain_dB:-6\n" ) # Set -6 dB on all components for OSBA to match IVAS_dec fp_meta.write(f"{in_fmt.split('_')[0][1:]}\n") # SBA or MASA - fp_meta.write(f"{n_obj+1}\n") + fp_meta.write(f"{n_obj + 1}\n") fp_meta.write(f"{in_fmt.split('_')[-1]}\n") # SBA or MASA parameter if "OMASA" in in_fmt: fp_meta.write( diff --git a/tests/renderer_short/utils.py b/tests/renderer_short/utils.py index 4c6e678ee7..27e58e4ede 100644 --- a/tests/renderer_short/utils.py +++ b/tests/renderer_short/utils.py @@ -67,7 +67,8 @@ from pyaudio3dtools.audiofile import readfile from ..cmp_pcm import cmp_pcm from ..conftest import get_split_idx, parse_properties -def _run_cmd(cmd, test_info=None, env=None ): + +def _run_cmd(cmd, test_info=None, env=None): """ Helper function for running some command. Raises a SystemError if either the return code is non-zero or a USAN printout is detected @@ -106,6 +107,7 @@ def run_ivas_isar_enc_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning IVAS ISAR encoder command\n{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) + def run_ivas_isar_dec_cmd(cmd, test_info=None, env=None): if BIN_SUFFIX_MERGETARGET in cmd[0]: logging.info(f"\nREF decoder command:\n\t{' '.join(cmd)}\n") @@ -113,6 +115,7 @@ def run_ivas_isar_dec_cmd(cmd, test_info=None, env=None): logging.info(f"\nDUT decoder command:\n\t{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) + def run_isar_post_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR post renderer command\n{' '.join(cmd)}\n") _run_cmd(cmd, test_info=test_info, env=env) @@ -426,7 +429,7 @@ def run_renderer( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) @@ -453,7 +456,7 @@ def run_renderer( odg_input=odg_input, odg_test=odg_test, odg_ref=odg_ref, - scalefac=test_info.config.option.scalefac, + mld_playback_level=test_info.config.option.mld_playback_level, split_idx=split_idx, ) output_differs_parts += output_differs_splits @@ -771,19 +774,19 @@ def scene_description_file(in_fmt, metadata_tmp, n_obj, input_file, in_meta_file metadata_tmp ).parent # File names must be relative to config file location fp_meta.write(f"{os.path.relpath(input_file, currdir)}\n") # Input file - fp_meta.write(f"{n_obj+1}\n") # Number of sources + fp_meta.write(f"{n_obj + 1}\n") # Number of sources for n in range(0, n_obj): if in_meta_files[n] == "NULL": md_file = "1\n1,0,0" # NULL metadata position: azim=0,elev=0 for 1 frame, looped throughout all frames. else: md_file = os.path.relpath(in_meta_files[n], currdir) - fp_meta.write(f"ISM\n{n+1}\n{md_file}\n") # ISM metadata + fp_meta.write(f"ISM\n{n + 1}\n{md_file}\n") # ISM metadata if "OSBA" in in_fmt: fp_meta.write( "gain_dB:-6\n" ) # Set -6 dB on all components for OSBA to match IVAS_dec fp_meta.write(f"{in_fmt.split('_')[0][1:]}\n") # SBA or MASA - fp_meta.write(f"{n_obj+1}\n") + fp_meta.write(f"{n_obj + 1}\n") fp_meta.write(f"{in_fmt.split('_')[-1]}\n") # SBA or MASA parameter if "OMASA" in in_fmt: fp_meta.write( diff --git a/tests/split_rendering/utils.py b/tests/split_rendering/utils.py index e9c86668a9..2f878c0eb4 100644 --- a/tests/split_rendering/utils.py +++ b/tests/split_rendering/utils.py @@ -62,6 +62,7 @@ from ..conftest import parse_properties sys.path.append(SCRIPTS_DIR) from pyaudio3dtools.audiofile import readfile, writefile + def lc3plus_used(test_info, in_fmt, render_config): return ( # LC3plus used explicitly @@ -147,6 +148,7 @@ def check_xfail( "unsupported framing: LCLD codec doesn't support aggregation. Pre-renderer (ISAR) frame size must match LCLD frame size." ) + def run_full_chain_split_rendering( record_property, props_to_record, @@ -271,7 +273,7 @@ def run_full_chain_split_rendering( dec_cmd[5:5] = ["-voip"] run_ivas_isar_dec_cmd(dec_cmd, test_info=test_info) - + # run split renderer post_rend_cmd = SPLIT_POST_REND_CMD[:] @@ -303,6 +305,7 @@ def run_full_chain_split_rendering( get_ssnr=get_ssnr, get_odg=get_odg, get_odg_bin=get_odg_bin, + mld_playback_level=test_info.config.option.mld_playback_level, ) # NOTE: split comparison not implemented for split rendering, always unpack values @@ -352,7 +355,7 @@ def run_external_split_rendering( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - + renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") filename_base = f"{in_fmt}_{renderer_fmt_for_filename}_ext_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}" @@ -446,6 +449,7 @@ def run_external_split_rendering( get_ssnr=get_ssnr, get_odg=get_odg, get_odg_bin=get_odg_bin, + mld_playback_level=test_info.config.option.mld_playback_level, ) # NOTE: split comparison not implemented for split rendering, always unpack values reason = reason[0] -- GitLab From ee8a3d92f382c4181176c396b66db5be721a4be4 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 16 Feb 2026 13:53:37 +0100 Subject: [PATCH 2/4] also pass playback level to PQevalAudio --- tests/cmp_pcm.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/cmp_pcm.py b/tests/cmp_pcm.py index e902d665f1..5d0dc94f5b 100755 --- a/tests/cmp_pcm.py +++ b/tests/cmp_pcm.py @@ -210,6 +210,7 @@ def pqevalaudio_wrapper( ref_sig: np.ndarray, eval_sig: np.ndarray, fs: int, + playback_level: float = 92.0, ) -> str: with tempfile.TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) @@ -231,7 +232,13 @@ def pqevalaudio_wrapper( pyaudio3dtools.audiofile.writefile(tmp_file_ref, r48, 48000) pyaudio3dtools.audiofile.writefile(tmp_file_eval, t48, 48000) - cmd = ["PQevalAudio", tmp_file_ref, tmp_file_eval] + cmd = [ + "PQevalAudio", + "--levelSPL", + f"{playback_level}", + tmp_file_ref, + tmp_file_eval, + ] result = subprocess.run(cmd, capture_output=True) if result.returncode != 0: -- GitLab From d8f9071a7f3ab751e4c0adac399715bd3b104774 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 17 Feb 2026 09:45:27 +0100 Subject: [PATCH 3/4] pass mld-playback-level to compare_dmx function --- tests/codec_be_on_mr_nonselection/test_param_file.py | 7 ++++++- tests/codec_be_on_mr_nonselection/test_sba.py | 12 +++++++++++- tests/conftest.py | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/codec_be_on_mr_nonselection/test_param_file.py b/tests/codec_be_on_mr_nonselection/test_param_file.py index 18e9f0a71c..c3ff6f6235 100644 --- a/tests/codec_be_on_mr_nonselection/test_param_file.py +++ b/tests/codec_be_on_mr_nonselection/test_param_file.py @@ -545,7 +545,12 @@ def run_test( str(f).replace(dut_base_path, reference_path) for f in dut_dmx_files ] - prop_results = compare_dmx_signals(ref_dmx_files, dut_dmx_files, in_sr) + prop_results = compare_dmx_signals( + ref_dmx_files, + dut_dmx_files, + in_sr, + test_info.config.options.mld_playback_level, + ) for prop, val in prop_results.items(): dut_decoder_frontend.record_property(prop, val) diff --git a/tests/codec_be_on_mr_nonselection/test_sba.py b/tests/codec_be_on_mr_nonselection/test_sba.py index f6b9be6e3f..8eeb55dd5c 100644 --- a/tests/codec_be_on_mr_nonselection/test_sba.py +++ b/tests/codec_be_on_mr_nonselection/test_sba.py @@ -180,6 +180,7 @@ def test_pca_enc( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if not encoder_only: @@ -337,6 +338,7 @@ def test_sba_enc_system( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if update_ref == 0 and get_enc_stats: @@ -490,6 +492,7 @@ def test_spar_hoa2_enc_system( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if update_ref == 0 and get_enc_stats: @@ -643,6 +646,7 @@ def test_spar_hoa3_enc_system( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if get_enc_stats: @@ -811,6 +815,7 @@ def test_sba_enc_BWforce_system( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if update_ref == 0 and get_enc_stats: @@ -1003,6 +1008,7 @@ def test_sba_plc_system( plc_pattern=plc_pattern, get_enc_stats=get_enc_stats, compare_enc_dmx=compare_enc_dmx, + mld_playback_level=test_info.config.option.mld_playback_level, ) if not encoder_only: @@ -1067,6 +1073,7 @@ def sba_enc( plc_pattern=None, get_enc_stats=False, compare_enc_dmx=False, + mld_playback_level=92.0, ): input_path = f"{test_vector_path}/{tag}.wav" dtx_mode = dtx == "1" @@ -1157,7 +1164,10 @@ def sba_enc( ] prop_results = compare_dmx_signals( - ref_dmx_files, dut_dmx_files, int(sampling_rate) + ref_dmx_files, + dut_dmx_files, + int(sampling_rate), + mld_playback_level, ) for prop, val in prop_results.items(): dut_encoder_frontend.record_property(prop, val) diff --git a/tests/conftest.py b/tests/conftest.py index f06fe3ce62..258fd80496 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1258,7 +1258,7 @@ class MaxDiffPatternNotFound(Exception): pass -def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: +def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs, mld_playback_level) -> dict: nchannels = len(dut_dmx_files) with NamedTemporaryFile(suffix=".ref_dmx.pcm") as dmx_file_ref_tmp: with NamedTemporaryFile(suffix=".dut_dmx.pcm") as dmx_file_dut_tmp: -- GitLab From fd6733ab9190fbe220422b01a070ab46d7bb0b97 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 17 Feb 2026 14:08:16 +0100 Subject: [PATCH 4/4] correctly pass mld_playback_level to peqval wrapper --- tests/cmp_pcm.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/cmp_pcm.py b/tests/cmp_pcm.py index 5d0dc94f5b..5eaec38ca8 100755 --- a/tests/cmp_pcm.py +++ b/tests/cmp_pcm.py @@ -148,7 +148,9 @@ def cmp_pcm( if get_odg: for n in range(nchannels): - pqeval_output = pqevalaudio_wrapper(s1[:, n], s2[:, n], fs) + pqeval_output = pqevalaudio_wrapper( + s1[:, n], s2[:, n], fs, mld_playback_level + ) match_odg = re.search(ODG_PATTERN_PQEVALAUDIO, pqeval_output) odg = float(match_odg.groups()[0]) @@ -170,7 +172,7 @@ def cmp_pcm( ).astype(np.int16) pqeval_output = pqevalaudio_wrapper( - odg_files[odg_input], odg_files[odg_ref], 48000 + odg_files[odg_input], odg_files[odg_ref], 48000, mld_playback_level ) match_odg = re.search(ODG_PATTERN_PQEVALAUDIO, pqeval_output) try: @@ -179,7 +181,7 @@ def cmp_pcm( raise OdgParsingFailed("Could not get Odg for ref signal") pqeval_output = pqevalaudio_wrapper( - odg_files[odg_input], odg_files[odg_test], 48000 + odg_files[odg_input], odg_files[odg_test], 48000, mld_playback_level ) match_odg = re.search(ODG_PATTERN_PQEVALAUDIO, pqeval_output) try: -- GitLab