diff --git a/tests/conftest.py b/tests/conftest.py index 5f7ed04a829219a8e5b1b968c93c8354268c85b7..cccae2e90662e37b3d34ec772c588074ce5b8d58 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -274,7 +274,7 @@ class EncoderFrontend: cmd_str = textwrap.indent(" ".join(command), prefix="\t") log_dbg_msg(f"{self._type} encoder command:\n{cmd_str}") - try: + try: result = run(command, capture_output=True, check=True, timeout=self.timeout) except TimeoutExpired: pytest.fail(f"{self._type} encoder run timed out after {self.timeout}s.") @@ -429,10 +429,10 @@ class DecoderFrontend: eid_path = "./scripts/tools/Darwin/eid-xor" else: raise ValueError(f'Wrong system "{system}"!') - + if not os.path.isfile(eid_path): raise FileNotFoundError(f"eid-xor binary {eid_path} not found!\n") - + eid_command = [eid_path] eid_command.extend(["-fer", "-vbr", "-bs", "g192", "-ep", "g192"]) eid_output_suffix = "." + next(tempfile._get_candidate_names()) + ".fer" @@ -441,7 +441,7 @@ class DecoderFrontend: str(plc_file), str(input_bitstream_path) + eid_output_suffix ] - + try: result = run(eid_command, check=True) except Exception as e: @@ -450,7 +450,7 @@ class DecoderFrontend: pytest.fail(f"eid-xor operation failed!") input_bitstream_path += eid_output_suffix - + if add_option_list is not None: command.extend(add_option_list) diff --git a/tests/hrtf_binary_loading/__init__.py b/tests/hrtf_binary_loading/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..462a690696adb655cb405d953ae30d04137ea868 --- /dev/null +++ b/tests/hrtf_binary_loading/__init__.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" diff --git a/tests/hrtf_binary_loading/bitstream/.gitignore b/tests/hrtf_binary_loading/bitstream/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f935021a8f8a7bd22f9d6703cafa5134bb6a57f8 --- /dev/null +++ b/tests/hrtf_binary_loading/bitstream/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/tests/hrtf_binary_loading/conftest.py b/tests/hrtf_binary_loading/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..4668d742db526a2758319beea8269f2d63412de9 --- /dev/null +++ b/tests/hrtf_binary_loading/conftest.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + + +def pytest_addoption(parser): + parser.addoption( + "--create_ref", + action="store_true", + default=False, + ) + parser.addoption( + "--create_cut", + action="store_true", + default=False, + ) diff --git a/tests/hrtf_binary_loading/constants.py b/tests/hrtf_binary_loading/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..223fa864f62f14365a2f30d0cf43a3d0956ccc05 --- /dev/null +++ b/tests/hrtf_binary_loading/constants.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +import re +from pathlib import Path + +from tests.renderer.constants import ( + OUTPUT_FORMATS_BINAURAL, + SCRIPTS_DIR, + TESTV_DIR, +) + +TESTS_DIR = Path(__file__).parent + +BITSTREAM_DIR = TESTS_DIR.joinpath("bitstream") +DEC_ROM_DIR = TESTS_DIR.joinpath("dec_out_rom") +HRTF_BINARY_DIR = SCRIPTS_DIR.joinpath( + "binauralRenderer_interface", "binaural_renderers_hrtf_data" +) +DEC_BINARY_DIR = TESTS_DIR.joinpath("dec_out_bin") + +ENCODER_CMD = [str(TESTS_DIR.parent.parent.joinpath("IVAS_cod"))] + +DECODER_CMD = [str(TESTS_DIR.parent.parent.joinpath("IVAS_dec"))] + +RENDERER_CMD = [str(TESTS_DIR.parent.parent.joinpath("IVAS_rend"))] + +HRTF_BINARY_FILE = "ivas_binaural_{}kHz.bin" +SAMPLE_RATE = ["16", "32", "48"] + +INPUT_FORMATS_MC = ["5_1", "5_1_2", "5_1_4", "7_1", "7_1_4"] # "MONO", "STEREO", +INPUT_FORMATS_SBA = ["1", "2", "3"] +INPUT_FORMATS_ISM = ["1", "2", "3", "4"] +INPUT_FORMATS_MASA = {"tc": ["1", "2"], "dir": ["1", "2"]} +INPUT_FORMATS_MASA_RENDERER = ["MASA1", "MASA2"] + + +FORMAT_TO_FILE_MC_WOEXT = { + "5_1": "stv51MC{}c", + "7_1": "stv71MC{}c", + "5_1_2": "stv512MC{}c", + "5_1_4": "stv514MC{}c", + "7_1_4": "stv714MC{}c", +} +FORMAT_TO_FILE_SBA_WOEXT = { + "1": "stvFOA{}c", + "2": "stv2OA{}c", + "3": "stv3OA{}c", +} +FORMAT_TO_FILE_ISM_WOEXT = "stv{}ISM{}s" +FORMAT_TO_FILE_MASA_WOEXT = "stv{}MASA{}TC{}c" + +BITRATE_ISM = { + "1": 96000, + "2": 160000, + "3": 384000, + "4": 512000, +} + +FORMAT_TO_METADATA_FILES = {"MASA": "stv{}MASA{}TC{}c.met", "ISM": "stvISM{}.csv"} + +FORMAT_TO_METADATA_FILES_RENDERER = { + "ISM1": [str(TESTV_DIR.joinpath("stvISM1.csv"))], + "ISM2": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + ], + "ISM3": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + str(TESTV_DIR.joinpath("stvISM3.csv")), + ], + "ISM4": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2.csv")), + str(TESTV_DIR.joinpath("stvISM3.csv")), + str(TESTV_DIR.joinpath("stvISM4.csv")), + ], + "NDP_ISM4": [ + str(TESTV_DIR.joinpath("stvISM1.csv")), + str(TESTV_DIR.joinpath("stvISM2_non-diegetic-pan.csv")), + str(TESTV_DIR.joinpath("stvISM3.csv")), + str(TESTV_DIR.joinpath("stvISM4.csv")), + ], + "MASA1": [str(TESTV_DIR.joinpath("stv1MASA1TC48c.met"))], + "MASA2": [str(TESTV_DIR.joinpath("stv2MASA2TC48c.met"))], +} + + +HR_TRAJECTORIES_TO_TEST = ["headrot_case00_3000_q", "headrot"] + +HR_TRAJECTORIES_TO_TEST_RENDERER = ["full_circle_in_15s"] + + +with open( + str(TESTS_DIR.parent.parent.joinpath("lib_com")) + "/ivas_cnst.h", "r" +) as ivas_cnst_file: + ivas_cnst = ivas_cnst_file.read() + + # Declare renderer_types + rtid = ivas_cnst.find("RENDERER_TYPE;") + rte_c = ivas_cnst[ivas_cnst[:rtid].rfind("{") : ivas_cnst[:rtid].rfind("}")] + enumid = 0 + for rtid1 in [m.start() for m in re.finditer("RENDERER_", rte_c)]: + rtid2 = rte_c[rtid1:].find(",") + if rtid2 == -1: + rtid2 = rte_c[rtid1:].find(" ") + globals()[rte_c[rtid1 : rtid1 + rtid2]] = enumid + enumid += 1 + + # Declare binaural_input_audio_config + biacid = ivas_cnst.find("BINAURAL_INPUT_AUDIO_CONFIG;") + biace_c = ivas_cnst[ivas_cnst[:biacid].rfind("{") : ivas_cnst[:biacid].rfind("}")] + enumid = 0 + for biacid1 in [ + m.start() for m in re.finditer("BINAURAL_INPUT_AUDIO_CONFIG_", biace_c) + ]: + biacid2 = biace_c[biacid1:].find(",") + if biacid2 == -1: + biacid2 = biace_c[biacid1:].find(" ") + globals()[biace_c[biacid1 : biacid1 + biacid2]] = enumid + enumid += 1 + +HRTF_READER_RENDERER_BINAURAL_INVALID = 0 +HRTF_READER_RENDERER_BINAURAL_FASTCONV = 1 +HRTF_READER_RENDERER_BINAURAL_FASTCONV_ROOM = 2 +HRTF_READER_RENDERER_BINAURAL_PARAMETRIC = 3 +HRTF_READER_RENDERER_BINAURAL_PARAMETRIC_ROOM = 4 +HRTF_READER_RENDERER_BINAURAL_OBJECTS_TD = 5 +HRTF_READER_RENDERER_BINAURAL_MIXER_CONV = 6 +HRTF_READER_RENDERER_BINAURAL_MIXER_CONV_ROOM = 7 +HRTF_READER_RENDERER_BINAURAL_REVERB_ALL = 8 + +BINAURAL_INPUT_AUDIO_CONFIG_INVALID = 0 +BINAURAL_INPUT_AUDIO_CONFIG_COMBINED = 1 +BINAURAL_INPUT_AUDIO_CONFIG_HOA3 = 2 +BINAURAL_INPUT_AUDIO_CONFIG_HOA2 = 3 +BINAURAL_INPUT_AUDIO_CONFIG_FOA = 4 +BINAURAL_INPUT_AUDIO_CONFIG_UNDEFINED = 5 + +REQ_HRTF_CONFIG = [ + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_FASTCONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_COMBINED, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_FASTCONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_HOA3, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_FASTCONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_HOA2, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_FASTCONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_FOA, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_FASTCONV_ROOM, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_COMBINED, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_PARAMETRIC_ROOM, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_UNDEFINED, + "sampling_frequency": 48000, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_OBJECTS_TD, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_UNDEFINED, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_MIXER_CONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_COMBINED, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_MIXER_CONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_HOA3, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_MIXER_CONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_HOA2, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_MIXER_CONV, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_FOA, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_MIXER_CONV_ROOM, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_COMBINED, + "sampling_frequency": -1, + }, + { + "renderer_type": HRTF_READER_RENDERER_BINAURAL_REVERB_ALL, + "input_configuration": BINAURAL_INPUT_AUDIO_CONFIG_UNDEFINED, + "sampling_frequency": -1, + }, +] diff --git a/tests/hrtf_binary_loading/dec_out_bin/.gitignore b/tests/hrtf_binary_loading/dec_out_bin/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f935021a8f8a7bd22f9d6703cafa5134bb6a57f8 --- /dev/null +++ b/tests/hrtf_binary_loading/dec_out_bin/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/tests/hrtf_binary_loading/dec_out_rom/.gitignore b/tests/hrtf_binary_loading/dec_out_rom/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f935021a8f8a7bd22f9d6703cafa5134bb6a57f8 --- /dev/null +++ b/tests/hrtf_binary_loading/dec_out_rom/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/tests/hrtf_binary_loading/test_codec_ROM_vs_file.py b/tests/hrtf_binary_loading/test_codec_ROM_vs_file.py new file mode 100644 index 0000000000000000000000000000000000000000..fbfd5fc637896e1f9bb0ea541419b6b58c5cb486 --- /dev/null +++ b/tests/hrtf_binary_loading/test_codec_ROM_vs_file.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" +import pytest + +from tests.hrtf_binary_loading.utils import * + +""" Binary file """ + + +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +def test_binary_file(test_info, out_fs): + check_binary_file(out_fs) + + +""" Multichannel """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_static(test_info, in_fmt, out_fmt, out_fs): + # if in_fmt in ["MONO", "STEREO"]: + # pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + # -mc InputConf : Multi-channel format + # where InputConf specifies the channel configuration (5_1, 7_1, 5_1_2, 5_1_4, 7_1_4) + + bitrate = 512000 + in_fs = 48 + option_list = ["-mc", in_fmt] + in_file = FORMAT_TO_FILE_MC_WOEXT[in_fmt].format(in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + ) + + +@pytest.mark.parametrize("trj_file", [HR_TRAJECTORIES_TO_TEST[0]]) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +def test_multichannel_binaural_headrotation( + test_info, in_fmt, out_fmt, out_fs, trj_file +): + # if in_fmt in ["MONO", "STEREO"]: + # pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + # -mc InputConf : Multi-channel format + # where InputConf specifies the channel configuration (5_1, 7_1, 5_1_2, 5_1_4, 7_1_4) + + bitrate = 512000 + in_fs = 48 + option_list = ["-mc", in_fmt] + in_file = FORMAT_TO_FILE_MC_WOEXT[in_fmt].format(in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + trj_file=str(TESTV_DIR.joinpath(f"{trj_file}.csv")), + ) + + +""" Ambisonics """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("fs", SAMPLE_RATE[1:]) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_SBA) +def test_sba_binaural_static(test_info, in_fmt, fs, out_fmt): + # -sba +/-Order : Scene Based Audio input format (Ambisonics ACN/SN3D) + # where Order specifies the Ambisionics order (1-3) + + bitrate = 256000 + option_list = ["-sba", in_fmt] + in_file = FORMAT_TO_FILE_SBA_WOEXT[in_fmt].format(fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + fs, + out_fmt, + fs, + ) + + +@pytest.mark.parametrize("trj_file", [HR_TRAJECTORIES_TO_TEST[0]]) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("fs", SAMPLE_RATE[1:]) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_SBA) +def test_sba_binaural_headrotation(test_info, in_fmt, fs, out_fmt, trj_file): + # -sba +/-Order : Scene Based Audio input format (Ambisonics ACN/SN3D) + # where Order specifies the Ambisionics order (1-3) + + bitrate = 256000 + option_list = ["-sba", in_fmt] + in_file = FORMAT_TO_FILE_SBA_WOEXT[in_fmt].format(fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + fs, + out_fmt, + fs, + trj_file=str(TESTV_DIR.joinpath(f"{trj_file}.csv")), + ) + + +""" MASA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("fs", SAMPLE_RATE[-1:]) +@pytest.mark.parametrize("in_dir", INPUT_FORMATS_MASA["dir"]) +@pytest.mark.parametrize("in_tc", INPUT_FORMATS_MASA["tc"]) +def test_masa_binaural_static(test_info, in_tc, in_dir, fs, out_fmt): + # -masa Ch File : MASA format + # where Ch specifies the number of MASA input/transport channels (1 or 2) + # and File specifies input file containing parametric MASA metadata + + bitrate = 256000 + metadata_file = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["MASA"].format(in_dir, in_tc, fs)) + ) + option_list = ["-masa", in_tc, metadata_file] + in_file = FORMAT_TO_FILE_MASA_WOEXT.format(in_dir, in_tc, fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + fs, + out_fmt, + fs, + ) + + +@pytest.mark.parametrize("trj_file", [HR_TRAJECTORIES_TO_TEST[1]]) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[:-1]) +@pytest.mark.parametrize("fs", SAMPLE_RATE[-1:]) +@pytest.mark.parametrize("in_dir", INPUT_FORMATS_MASA["dir"]) +@pytest.mark.parametrize("in_tc", INPUT_FORMATS_MASA["tc"]) +def test_masa_binaural_headrotation(test_info, in_tc, in_dir, fs, out_fmt, trj_file): + # -masa Ch File : MASA format + # where Ch specifies the number of MASA input/transport channels (1 or 2) + # and File specifies input file containing parametric MASA metadata + + bitrate = 256000 + metadata_file = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["MASA"].format(in_dir, in_tc, fs)) + ) + option_list = ["-masa", in_tc, metadata_file] + in_file = FORMAT_TO_FILE_MASA_WOEXT.format(in_dir, in_tc, fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + fs, + out_fmt, + fs, + trj_file=str(TESTV_DIR.joinpath(f"{trj_file}.csv")), + ) + + +""" ISM """ + + +@pytest.mark.parametrize("out_fmt", [OUTPUT_FORMATS_BINAURAL[0]]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_static(test_info, in_fmt, out_fs, out_fmt): + # -ism [+]Ch Files : ISM format + # where Ch specifies the number of ISMs (1-4) + # and Files specify input files containing metadata, one file per object + + in_fs = 48 + bitrate = BITRATE_ISM[in_fmt] + option_list = ["-ism", in_fmt] + metadata_file_list = [] + for n in range(int(in_fmt)): + test = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["ISM"].format(n + 1, in_fs)) + ) + metadata_file_list.append(test) + option_list.extend(metadata_file_list) + in_file = FORMAT_TO_FILE_ISM_WOEXT.format(in_fmt, in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + ) + + +@pytest.mark.parametrize("trj_file", [HR_TRAJECTORIES_TO_TEST[0]]) +@pytest.mark.parametrize("out_fmt", [OUTPUT_FORMATS_BINAURAL[0]]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_headrotation(test_info, in_fmt, out_fs, out_fmt, trj_file): + # -ism [+]Ch Files : ISM format + # where Ch specifies the number of ISMs (1-4) + # and Files specify input files containing metadata, one file per object + + in_fs = 48 + bitrate = BITRATE_ISM[in_fmt] + option_list = ["-ism", in_fmt] + metadata_file_list = [] + for n in range(int(in_fmt)): + test = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["ISM"].format(n + 1, in_fs)) + ) + metadata_file_list.append(test) + option_list.extend(metadata_file_list) + in_file = FORMAT_TO_FILE_ISM_WOEXT.format(in_fmt, in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + trj_file=str(TESTV_DIR.joinpath(f"{trj_file}.csv")), + ) + + +""" ISM - Room Reverb """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[-1:]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_roomreverb_static(test_info, in_fmt, out_fs, out_fmt): + # -ism [+]Ch Files : ISM format + # where Ch specifies the number of ISMs (1-4) + # and Files specify input files containing metadata, one file per object + + in_fs = 48 + bitrate = BITRATE_ISM[in_fmt] + option_list = ["-ism", in_fmt] + metadata_file_list = [] + for n in range(int(in_fmt)): + test = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["ISM"].format(n + 1, in_fs)) + ) + metadata_file_list.append(test) + option_list.extend(metadata_file_list) + in_file = FORMAT_TO_FILE_ISM_WOEXT.format(in_fmt, in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + ) + + +@pytest.mark.parametrize("trj_file", [HR_TRAJECTORIES_TO_TEST[0]]) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[-1:]) +@pytest.mark.parametrize("out_fs", SAMPLE_RATE) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +def test_ism_binaural_roomreverb_headrotation( + test_info, in_fmt, out_fs, out_fmt, trj_file +): + # -ism [+]Ch Files : ISM format + # where Ch specifies the number of ISMs (1-4) + # and Files specify input files containing metadata, one file per object + + in_fs = 48 + bitrate = BITRATE_ISM[in_fmt] + option_list = ["-ism", in_fmt] + metadata_file_list = [] + for n in range(int(in_fmt)): + test = str( + TESTV_DIR.joinpath(FORMAT_TO_METADATA_FILES["ISM"].format(n + 1, in_fs)) + ) + metadata_file_list.append(test) + option_list.extend(metadata_file_list) + in_file = FORMAT_TO_FILE_ISM_WOEXT.format(in_fmt, in_fs) + + compare_rom_vs_binary( + test_info, + option_list, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + trj_file=str(TESTV_DIR.joinpath(f"{trj_file}.csv")), + ) diff --git a/tests/hrtf_binary_loading/test_renderer_ROM_vs_file.py b/tests/hrtf_binary_loading/test_renderer_ROM_vs_file.py new file mode 100644 index 0000000000000000000000000000000000000000..1fd7bbbe3e4c6f58c4fc5e303e179f0a6b9c6d99 --- /dev/null +++ b/tests/hrtf_binary_loading/test_renderer_ROM_vs_file.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +import pytest + +from tests.hrtf_binary_loading.utils import * + +from tests.renderer.constants import ( + INPUT_FORMATS_AMBI, + INPUT_FORMATS_ISM, + INPUT_FORMATS_MC, + CUSTOM_LS_TO_TEST, + CUSTOM_LAYOUT_DIR, + FRAMING_TO_TEST, + HR_TRAJECTORY_DIR, +) + +""" Ambisonics """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_ambisonics_binaural_static_with_binary_hrir( + test_info, in_fmt, out_fmt, frame_size +): + compare_renderer_vs_renderer_with_binary_hrir( + test_info, in_fmt, out_fmt, frame_size=frame_size + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST_RENDERER) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_ambisonics_binaural_headrotation_with_binary_hrir( + test_info, in_fmt, out_fmt, trj_file, frame_size +): + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + frame_size=frame_size, + ) + + +""" Multichannel """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_multichannel_binaural_static_with_binary_hrir( + test_info, in_fmt, out_fmt, frame_size +): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, in_fmt, out_fmt, frame_size=frame_size + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST_RENDERER) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_multichannel_binaural_headrotation_with_binary_hrir( + test_info, in_fmt, out_fmt, trj_file, frame_size +): + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + frame_size=frame_size, + ) + + +""" ISM """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_ism_binaural_static_with_binary_hrir(test_info, in_fmt, out_fmt, frame_size): + try: + in_meta_files = FORMAT_TO_METADATA_FILES_RENDERER[in_fmt] + except: + in_meta_files = None + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, in_fmt, out_fmt, in_meta_files=in_meta_files, frame_size=frame_size + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST_RENDERER) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_ism_binaural_headrotation_with_binary_hrir( + test_info, in_fmt, out_fmt, trj_file, frame_size +): + try: + in_meta_files = FORMAT_TO_METADATA_FILES_RENDERER[in_fmt] + except: + in_meta_files = None + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + in_meta_files=in_meta_files, + frame_size=frame_size, + ) + + +""" MASA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA_RENDERER) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_masa_binaural_static_with_binary_hrir(test_info, in_fmt, out_fmt, frame_size): + if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: + pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + in_meta_files=FORMAT_TO_METADATA_FILES_RENDERER[in_fmt], + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST_RENDERER) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA_RENDERER) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_masa_binaural_headrotation_with_binary_hrir( + test_info, in_fmt, out_fmt, trj_file, frame_size +): + if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: + pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") + + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + in_meta_files=FORMAT_TO_METADATA_FILES_RENDERER[in_fmt], + ) + + +""" Custom loudspeaker layouts """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_custom_ls_input_binaural_with_binary_hrir( + test_info, in_layout, out_fmt, frame_size +): + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + frame_size=frame_size, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST_RENDERER) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +def test_custom_ls_input_binaural_headrotation_with_binary_hrir( + test_info, in_layout, out_fmt, trj_file, frame_size +): + compare_renderer_vs_renderer_with_binary_hrir( + test_info, + CUSTOM_LAYOUT_DIR.joinpath(f"{in_layout}.txt"), + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + frame_size=frame_size, + ) diff --git a/tests/hrtf_binary_loading/utils.py b/tests/hrtf_binary_loading/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..66d177541f986d76179fa4009e5e912138396b3d --- /dev/null +++ b/tests/hrtf_binary_loading/utils.py @@ -0,0 +1,391 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. +""" + +import os +import sys +import uuid +from typing import Dict, Optional + +import pytest + +from tests.renderer.compare_audio import compare_audio_arrays +from tests.renderer.utils import check_BE, run_cmd, test_info, run_renderer + +from .constants import * + +sys.path.append(SCRIPTS_DIR) +import pyaudio3dtools + + +def run_encoder( + bitrate: int, + sampling_rate: int, + input_file: str, + output_file: str, + sba_order: Optional[str] = None, + dtx_mode: Optional[bool] = False, + max_band: Optional[str] = None, + bypass_mode: Optional[int] = None, + quiet_mode: Optional[bool] = True, + add_option_list: Optional[list] = None, +): + cmd = ENCODER_CMD[:] + + # add optional parameters + + if sba_order is not None: + cmd.extend(["-sba", sba_order]) + + if dtx_mode: + cmd.extend(["-dtx"]) + + if max_band is not None: + cmd.extend(["-max_band", max_band]) + + if bypass_mode is not None: + cmd.extend(["-bypass", str(bypass_mode)]) + + if quiet_mode: + cmd.extend(["-q"]) + + if add_option_list is not None: + cmd.extend(add_option_list) + + # add mandatory parameters + + cmd += [ + str(bitrate), + str(sampling_rate), + str(input_file), + str(output_file), + ] + + run_cmd(cmd) + + +def run_decoder( + output_config: str, + output_sampling_rate: int, + input_bitstream_path: Path, + output_path: Path, + quiet_mode: Optional[bool] = True, + plc_file: Optional[Path] = None, + add_option_list: Optional[list] = None, +): + cmd = DECODER_CMD[:] + + # add optional parameters + + if quiet_mode: + cmd.extend(["-q"]) + + if plc_file is not None: + cmd.extend(["-fec", str(plc_file)]) + + if add_option_list is not None: + cmd.extend(add_option_list) + + # add mandatory parameters + + # output_config is mandatory for IVAS; EVS does not have this parameter, indicated by "" + if output_config != "": + cmd += [output_config] + + cmd += [ + str(output_sampling_rate), + str(input_bitstream_path), + str(output_path), + ] + + run_cmd(cmd) + + +def get_option_list_str(option_list): + option_list_str = option_list.copy() + for i in range(len(option_list_str)): + if option_list_str[i][0] == "-": + option_list_str[i] = option_list_str[i][1:] + if os.path.isfile(option_list_str[i]): + option_list_str[i] = Path(option_list_str[i]).stem + + return option_list_str + + +def check_binary_file(out_fs): + with open( + str(HRTF_BINARY_DIR.joinpath(HRTF_BINARY_FILE.format(out_fs))), "rb" + ) as file: + binary_data = file.read() + + # [Declaration of the binary file] + # File Identifier (8 bytes) + # Size of file in bytes (4 bytes) + # Number of HRTF (2 bytes) + # Max length of HRTF data (4 bytes) + + binary_file_size = os.path.getsize( + str(HRTF_BINARY_DIR.joinpath(HRTF_BINARY_FILE.format(out_fs))) + ) + if binary_file_size < 18: + pytest.fail("HRTF binary file not compliant (size of file header)") + + file_header = binary_data[:18] + + file_identifier = file_header[:8].decode() + if file_identifier != "IVASHRTF": + pytest.fail("Header of HRTF binary file not compliant (identifier)") + + file_size = int.from_bytes(file_header[8:12], byteorder="little") + if file_size != binary_file_size: + pytest.fail("Header of HRTF binary file not compliant (file size)") + + nb_hrtf = int.from_bytes(file_header[12:14], byteorder="little") + max_data_size = int.from_bytes(file_header[14:18], byteorder="little") + + read_bid = 18 + + hrtf_cnt = 0 + raw_data_size_max = 0 + hrtf_config_file = [] + while read_bid < file_size: + if read_bid + 16 > file_size: + pytest.fail("Reading of HRTF header failed") + + # HRTF Header */ + # Renderer type (4 bytes) : See "RENDERER_TYPE" */ + # Input configuration (4 bytes) : See "BINAURAL_INPUT_AUDIO_CONFIG" */ + # Sampling Frequency (4 bytes) */ + # Raw data size (4 bytes) */ + + hrtf_header = binary_data[read_bid : read_bid + 16] + + renderer_type = int.from_bytes(hrtf_header[:4], byteorder="little") + input_configuration = int.from_bytes(hrtf_header[4:8], byteorder="little") + sampling_frequency = int.from_bytes(hrtf_header[8:12], byteorder="little") + raw_data_size = int.from_bytes(hrtf_header[12:16], byteorder="little") + + if ( + (renderer_type != HRTF_READER_RENDERER_BINAURAL_MIXER_CONV) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_MIXER_CONV_ROOM) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_FASTCONV) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_FASTCONV_ROOM) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_PARAMETRIC) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_PARAMETRIC_ROOM) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_OBJECTS_TD) + and (renderer_type != HRTF_READER_RENDERER_BINAURAL_REVERB_ALL) + ): + pytest.fail("Header of HRTF binary file not compliant (renderer type)") + + if ( + (input_configuration != BINAURAL_INPUT_AUDIO_CONFIG_COMBINED) + and (input_configuration != BINAURAL_INPUT_AUDIO_CONFIG_HOA3) + and (input_configuration != BINAURAL_INPUT_AUDIO_CONFIG_HOA2) + and (input_configuration != BINAURAL_INPUT_AUDIO_CONFIG_FOA) + and (input_configuration != BINAURAL_INPUT_AUDIO_CONFIG_UNDEFINED) + ): + pytest.fail( + "Header of HRTF binary file not compliant (input configuration)" + ) + + if ( + (sampling_frequency != 16000) + and (sampling_frequency != 32000) + and (sampling_frequency != 48000) + ): + pytest.fail( + "Header of HRTF binary file not compliant (sampling frequency)" + ) + + if raw_data_size_max < raw_data_size: + raw_data_size_max = raw_data_size + + hrtf_config_file.append( + { + "renderer_type": renderer_type, + "input_configuration": input_configuration, + "sampling_frequency": sampling_frequency, + } + ) + + # hrtf_raw_data => Tested in compare_rom_vs_binary + read_bid += 16 + raw_data_size + + hrtf_cnt += 1 + + if read_bid != file_size: + pytest.fail("Inconsistent size of binary file") + + if hrtf_cnt != nb_hrtf: + pytest.fail("Inconsistent number of HRTF in binary file") + + if raw_data_size_max != max_data_size: + pytest.fail("Inconsistent max data size") + + for hrtf_config in REQ_HRTF_CONFIG: + config = next( + ( + cfg + for cfg in hrtf_config_file + if (cfg["renderer_type"] == hrtf_config["renderer_type"]) + and ( + cfg["input_configuration"] == hrtf_config["input_configuration"] + ) + ), + None, + ) + if config == None: + pytest.fail( + f"Configuration (renderer_type : {hrtf_config['renderer_type']} - input_configuration : {hrtf_config['input_configuration']}) not found in binary file!" + ) + if ( + (hrtf_config["sampling_frequency"] == -1) + and (config["sampling_frequency"] != int(out_fs) * 1000) + ) or ( + (hrtf_config["sampling_frequency"] != -1) + and (config["sampling_frequency"] != hrtf_config["sampling_frequency"]) + ): + pytest.fail( + f"Bad sampling frequency for configuration (renderer_type : {hrtf_config['renderer_type']} - input_configuration : {hrtf_config['input_configuration']}) : {config['sampling_frequency']} instead of {int(out_fs)*1000}!" + ) + + +def compare_rom_vs_binary( + test_info, + option_list_enc, + bitrate, + in_file, + in_fs, + out_fmt, + out_fs, + keep_file: Optional[bool] = False, + trj_file: Optional[str] = None, +): + option_str = "_".join(get_option_list_str(option_list_enc)) + file_ext = f"_{option_str or ''}_{bitrate or ''}_{in_fs or ''}-{out_fs or ''}_{out_fmt or ''}-{uuid.uuid1()} " + + # check_binary_file(out_fs) + + input_path = TESTV_DIR.joinpath(in_file).with_suffix(".wav") + bitstream_path = BITSTREAM_DIR.joinpath(in_file + file_ext) + run_encoder( + bitrate, in_fs, input_path, bitstream_path, add_option_list=option_list_enc + ) + + if trj_file is not None: + option_list_dec = ["-t", trj_file] + file_ext += "_headrot" + else: + option_list_dec = None + out_rom_path = DEC_ROM_DIR.joinpath(in_file + file_ext).with_suffix(".wav") + run_decoder( + out_fmt, out_fs, bitstream_path, out_rom_path, add_option_list=option_list_dec + ) + out_rom, out_rom_fs = pyaudio3dtools.audiofile.readfile(out_rom_path) + + hrtf_file = HRTF_BINARY_FILE.format(out_fs) + if trj_file is not None: + option_list_dec.extend(["-hrtf", str(HRTF_BINARY_DIR.joinpath(hrtf_file))]) + else: + option_list_dec = ["-hrtf", str(HRTF_BINARY_DIR.joinpath(hrtf_file))] + out_bin_path = DEC_BINARY_DIR.joinpath(in_file + file_ext).with_suffix(".wav") + run_decoder( + out_fmt, out_fs, bitstream_path, out_bin_path, add_option_list=option_list_dec + ) + out_bin, out_bin_fs = pyaudio3dtools.audiofile.readfile(out_rom_path) + + check_BE(test_info, out_rom, out_rom_fs, out_bin, out_bin_fs) + if keep_file == False: + os.remove(bitstream_path) + os.remove(out_rom_path) + os.remove(out_bin_path) + + +def compare_renderer_vs_renderer_with_binary_hrir( + test_info, + in_fmt, + out_fmt, + metadata_input: Optional[str] = None, + in_meta_files: Optional[list] = None, + trj_file: Optional[str] = None, + non_diegetic_pan: Optional[str] = None, + name_extension: Optional[str] = uuid.uuid1(), + refrot_file: Optional[str] = None, + refvec_file: Optional[str] = None, + refveclev_file: Optional[str] = None, + config_file: Optional[str] = None, + frame_size: Optional[str] = "20ms", + hrir_name="ivas_binaural_48kHz.bin", + keep_file=False, +): + hrtf_file_dir = SCRIPTS_DIR.joinpath( + "binauralRenderer_interface/binaural_renderers_hrtf_data" + ) + hrtf_file_path = hrtf_file_dir.joinpath(hrir_name) + ref_out = run_renderer( + test_info, + in_fmt, + out_fmt, + metadata_input, + in_meta_files, + trj_file, + non_diegetic_pan, + name_extension, + refrot_file, + refvec_file, + refveclev_file, + config_file, + frame_size=frame_size, + ) + cut_out = run_renderer( + test_info, + in_fmt, + out_fmt, + metadata_input, + in_meta_files, + trj_file, + non_diegetic_pan, + name_extension, + refrot_file, + refvec_file, + refveclev_file, + config_file, + hrtf_file=hrtf_file_path, + frame_size=frame_size, + ) + + ref, ref_fs = pyaudio3dtools.audiofile.readfile(ref_out) + cut, cut_fs = pyaudio3dtools.audiofile.readfile(cut_out) + + check_BE(test_info, ref, ref_fs, cut, cut_fs) + if keep_file == False: + os.remove(ref_out) + os.remove(cut_out) diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py index 5f724e16de66c2d1b251000208e1a80ec6b9acdf..1c8e0f2198b619a37185ccc6e9a7003607a4acf2 100644 --- a/tests/renderer/utils.py +++ b/tests/renderer/utils.py @@ -108,6 +108,7 @@ def run_renderer( config_file: Optional[str] = None, binary_suffix: str = "", frame_size: Optional[str] = "20ms", + hrtf_file: Optional[str] = None, ) -> str: # prepare arguments and filepaths if trj_file is not None: @@ -145,6 +146,11 @@ def run_renderer( else: out_name = out_fmt + if hrtf_file is not None: + hrtf_file_name = f"_{hrtf_file.stem}" + else: + hrtf_file_name = "" + if test_info.config.option.create_ref: output_path_base = OUTPUT_PATH_REF else: @@ -166,7 +172,7 @@ def run_renderer( in_file = FORMAT_TO_FILE[in_fmt] in_name = in_fmt - out_file_stem = f"{in_name}_to_{out_name}{trj_name}{non_diegetic_pan}{refrot_name}{refvec_name}{refveclev_name}{config_name}{framing_name}{name_extension}.wav" + out_file_stem = f"{in_name}_to_{out_name}{trj_name}{non_diegetic_pan}{refrot_name}{refvec_name}{refveclev_name}{config_name}{framing_name}{hrtf_file_name}{name_extension}.wav" out_file = str(output_path_base.joinpath(out_file_stem))