From 4bebfade0fbb1b54d6275755415d49218a76bce8 Mon Sep 17 00:00:00 2001 From: vaclav Date: Tue, 18 Nov 2025 09:44:12 +0100 Subject: [PATCH 01/40] update --- readme.txt | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/readme.txt b/readme.txt index 62d21f51ba..7e82dc2c57 100644 --- a/readme.txt +++ b/readme.txt @@ -38,7 +38,8 @@ in ISO/IEC C99. The system is implemented as six separate programs: IVAS_cod IVAS Encoder IVAS_dec IVAS Decoder IVAS_rend IVAS External Renderer - IVAS_cod_fmtsw IVAS Encoder with support for format switching + ISAR_post_rend ISAR Post Renderer + IVAS_cod_fmtsw IVAS Encoder with support for format switching ambi_converter example program for Ambisonics format conversion For encoding using the coder program, the input is a binary @@ -124,7 +125,7 @@ should have the following structure: . `-- c-code |-- readme.txt - |-- Makefile + |-- Makefile |-- Workspace_msvc |-- apps |-- lib_com @@ -133,8 +134,9 @@ should have the following structure: |-- lib_enc |-- lib_isar |-- lib_lc3plus - |-- lib_rend + |-- lib_rend |-- lib_util + |-- scripts The package includes a Makefile for gcc, which has been verified on 32-bit Linux systems. The code can be compiled by entering the directory @@ -150,7 +152,7 @@ To compile the code, please open "Workspace_msvc\Workspace_msvc.sln" and build "encoder" for the encoder, "decoder" for the decoder, and "renderer" for the renderer executable. The resulting encoder/decoder/renderer/ISAR_post_renderer executables are "IVAS_cod.exe", "IVAS_dec.exe", "IVAS_rend.exe", and -"ISAR_post_rend.exe". All reside in the c-code main directory. In addition, this +"ISAR_post_rend.exe". All reside in the c-code main directory. In addition, this directory will contain a version of the encoder with support for format switching (named "IVAS_cod_fmtsw.exe") and an example program for Ambisonics format conversion (named "ambi_converter.exe"). @@ -294,7 +296,7 @@ Options: EVS RTP Payload Format or rtpdump files containing TS26.253 Annex A IVAS RTP Payload Format. The SDP parameter hf_only is required. Reading RFC4867 AMR/AMR-WB RTP payload format is not supported. --Tracefile TF : VoIP mode: Generate trace file named TF. Requires -no_delay_cmp to +-Tracefile TF : VoIP mode: Generate trace file named TF. Requires -no_delay_cmp to be enabled so that trace contents remain in sync with audio output. -fec_cfg_file : Optimal channel aware configuration computed by the JBM as described in Section 6.3.1 of TS26.448. The output is @@ -306,7 +308,7 @@ Options: Format files, the magic word in the mime file is used to determine which of the two supported formats is in use. default bitstream file format is G.192 --fr L : render frame size in ms L=(5,10,20), default is 20 +-fr L : render frame size in ms L=(5,10,20), default is 20 -hrtf File : HRTF filter File used in BINAURAL rendering -T File : Head rotation specified by external trajectory File -otr tracking_type : Head orientation tracking type: 'none', 'ref', 'avg', 'ref_vec' @@ -358,18 +360,18 @@ Options: -render_config File : Binaural renderer configuration parameters in File (only for binaural outputs) -room_size (S|M|L) : Selects default reverb based on a room size (S - small | M - medium | L - large) -non_diegetic_pan P : Panning mono non-diegetic sound to stereo -90<= P <= 90 - left or l or 90->left, right or r or -90->right, center or c or 0 ->middle + left or l or 90->left, right or r or -90->right, center or c or 0 ->middle -exof File : External orientation trajectory File for simulation of external orientations -dpid ID : Directivity pattern ID(s) (space-separated list of up to 4 numbers can be - specified) for binaural outputs + specified) for binaural output configurations -aeid ID | File : Acoustic environment ID (number > 0) alternatively, it can be a text file where each line contains "ID duration" for BINAURAL_ROOM_REVERB output. -lp Position : Output LFE position. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear (like --gain, -g) and azimuth, elevation are in degrees. - If specified, overrides the default behavior which attempts to map input to output LFE channel(s) + If specified, overrides the default behavior which attempts to map input to output LFE channel(s) -lm File : LFE panning matrix File (CSV table) containing a matrix of dimensions [ num_input_lfe x num_output_channels ] with elements specifying linear routing gain (like --gain, -g). - If specified, overrides the output LFE position option and the default behavior which attempts to map input to output LFE channel(s) + If specified, overrides the output LFE position option and the default behavior which attempts to map input to output LFE channel(s) -no_delay_cmp : Turn off delay compensation -g : Input gain (linear, not in dB) to be applied to input audio file -l : List supported audio formats @@ -381,8 +383,8 @@ Options: -q : Quiet mode, limit printouts to terminal, default is deactivated -The usage of the "ISAR_post_rend" program as follows: ------------------------------------------------------ +The usage of the "ISAR_post_rend" program is as follows: +-------------------------------------------------------- Usage: ISAR_post_rend [options] @@ -396,6 +398,33 @@ Options: -prbfi File : BFI File +The usage of the "ambi_converter" program is as follows: +-------------------------------------------------------- + +Usage: ambi_converter input_file output_file input_convention output_convention + +input_convention and output convention must be an integer number in [0,5] +the following conventions are supported: +0 : ACN-SN3D +1 : ACN-N3D +2 : FuMa-MaxN +3 : FuMa-FuMa +4 : SID-SN3D +5 : SID-N3D + +Either the input or the output convention must always be ACN-SN3D. + +The usage of the "IVAS_cod_fmtsw" program is as follows: +-------------------------------------------------------- + +Usage: IVAS_cod_fmtsw format_switching_file + +Mandatory parameters: +--------------------- +format_switching_file: Text file containing a valid encoder command line in each line + + + MULTICHANNEL LOUDSPEAKER INPUT / OUTPUT CONFIGURATIONS ====================================================== The loudspeaker positions for each MC layouts are assumed to have the following azimuth and elevation @@ -423,31 +452,6 @@ omitted, the LFE input is downmixed to all channels with a factor of 1/N. Positi the LFE channel. Maximum number of supported loudskpeakers N is 16. An example custom loudspeaker layout file is available: ls_setup_16ch_8+4+4.txt -The usage of the "ambi_converter" program as follows: ------------------------------------------------------ - -Usage: ambi_converter input_file output_file input_convention output_convention - -input_convention and output convention must be an integer number in [0,5] -the following conventions are supported: -0 : ACN-SN3D -1 : ACN-N3D -2 : FuMa-MaxN -3 : FuMa-FuMa -4 : SID-SN3D -5 : SID-N3D - -Either the input or the output convention must always be ACN-SN3D. - -The usage of the "IVAS_cod_fmtsw" program is as follows: --------------------------------------------------------- - -Usage: IVAS_cod_fmtsw format_switching_file - -Mandatory parameters: ---------------------- -format_switching_file: Text file containing a valid encoder command line in each line - RUNNING THE SELF TEST ===================== -- GitLab From 746b623134d90246a64ce897c40aea32202e381d Mon Sep 17 00:00:00 2001 From: vaclav Date: Tue, 18 Nov 2025 09:57:43 +0100 Subject: [PATCH 02/40] update --- readme.txt | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/readme.txt b/readme.txt index 7e82dc2c57..3147d457eb 100644 --- a/readme.txt +++ b/readme.txt @@ -324,14 +324,13 @@ Options: left or l or 90->left, right or r or -90->right, center or c or 0->middle -exof File : External orientation trajectory File for simulation of external orientations -dpid ID : Directivity pattern ID(s) (space-separated list of up to 4 numbers can be - specified) for binaural output configuration --aeid ID | File : Acoustic environment ID (number > 0) or - alternatively, it can be a text file where each line contains "ID duration" - for BINAURAL_ROOM_REVERB output configuration. + specified) for binaural output configurations +-aeid ID | File : Acoustic environment ID (number > 0) or a text file where each line + contains "ID duration" for BINAURAL_ROOM_REVERB output configuration -obj_edit File : Object editing instructions file or NULL for built-in example --level level : Complexity level, level = (1, 2, 3), will be defined after characterisation. --om File : Coded metadata File for BINAURAL_SPLIT_PCM OutputConf - Currently, all values default to level 3 (full functionality). +-level level : Complexity level, level = (1, 2, 3), will be defined after characterisation + Currently, all values default to level 3 (full functionality) +-om File : Coded metadata File for BINAURAL_SPLIT_PCM output configuration -q : Quiet mode, limit printouts to terminal, default is deactivated @@ -364,10 +363,10 @@ Options: -exof File : External orientation trajectory File for simulation of external orientations -dpid ID : Directivity pattern ID(s) (space-separated list of up to 4 numbers can be specified) for binaural output configurations --aeid ID | File : Acoustic environment ID (number > 0) - alternatively, it can be a text file where each line contains "ID duration" for BINAURAL_ROOM_REVERB output. +-aeid ID | File : Acoustic environment ID (number > 0) or a text file where each line + contains "ID duration" for BINAURAL_ROOM_REVERB output configuration -lp Position : Output LFE position. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear - (like --gain, -g) and azimuth, elevation are in degrees. + (like --gain, -g) and azimuth, elevation are in degrees If specified, overrides the default behavior which attempts to map input to output LFE channel(s) -lm File : LFE panning matrix File (CSV table) containing a matrix of dimensions [ num_input_lfe x num_output_channels ] with elements specifying linear routing gain (like --gain, -g). @@ -376,9 +375,8 @@ Options: -g : Input gain (linear, not in dB) to be applied to input audio file -l : List supported audio formats -smd : Metadata Synchronization Delay in ms, Default is 0. Quantized by 5ms subframes. --om File : Coded metadata File (only for BINAURAL_SPLIT_PCM output) --prbfi File : BFI File (only for BINAURAL_SPLIT_PCM output) --level level : Complexity level, level = (1, 2, 3), will be defined after characterisation. +-om File : Coded metadata File for BINAURAL_SPLIT_PCM output configuration +-level level : Complexity level, level = (1, 2, 3), will be defined after characterisation Currently, all values default to level 3 (full functionality). -q : Quiet mode, limit printouts to terminal, default is deactivated @@ -414,6 +412,7 @@ the following conventions are supported: Either the input or the output convention must always be ACN-SN3D. + The usage of the "IVAS_cod_fmtsw" program is as follows: -------------------------------------------------------- @@ -695,17 +694,17 @@ The parameters for the object editing in decoder for the supported formats can b parameter file. Each row of the file corresponds to one 20 ms IVAS frame. The row contains one or more of the following parameters separated by a comma: -bg_gain= linear gain to be applied on the SBA/MASA component in OSBA/OMASA, no effect for ISM -obj__gain= linear gain to be applied on object , 0-based indexing -obj__relgain=0|1 if 1, obj__gain is interpreted as a relative modification. default is absolute modification -obj__azi= azimuth angle in degrees to be applied on object , 0-based indexing -obj__relazi=0|1 if 1, obj__azi is interpreted as a relative modification. default is absolute modification -obj__ele= elevation angle in degrees to be applied on object , 0-based indexing -obj__relele=0|1 if 1, obj__ele is interpreted as a relative modification. default is absolute modification +bg_gain= linear gain to be applied on the SBA/MASA component in OSBA/OMASA, no effect for ISM +obj__gain= linear gain to be applied on object , 0-based indexing +obj__relgain=0|1 if 1, obj__gain is interpreted as a relative modification. default is absolute modification +obj__azi= azimuth angle in degrees to be applied on object , 0-based indexing +obj__relazi=0|1 if 1, obj__azi is interpreted as a relative modification. default is absolute modification +obj__ele= elevation angle in degrees to be applied on object , 0-based indexing +obj__relele=0|1 if 1, obj__ele is interpreted as a relative modification. default is absolute modification obj__radius= linear radius to be applied on object , 0-based indexing obj__relradius=0|1 if 1, obj__radius is interpreted as a relative modification. default is absolute modification -obj__yaw= yaw angle in degrees to be applied on object , 0-based indexing -obj__relyaw=0|1 if 1, obj__yaw is interpreted as a relative modification. default is absolute modification +obj__yaw= yaw angle in degrees to be applied on object , 0-based indexing +obj__relyaw=0|1 if 1, obj__yaw is interpreted as a relative modification. default is absolute modification obj__pitch= pitch angle in degrees to be applied on object , 0-based indexing obj__relpitch=0|1 if 1, obj__pitch is interpreted as a relative modification. default is absolute modification @@ -724,4 +723,3 @@ typedef struct { u_int32 length; /* size of the RTP packet in bytes */ (u_int8 * length) RTP_packet; /* RTP packet (sized length * byte) */ } RTP_streaming_packet; - -- GitLab From 8ba891a6954a60079030415fedbfba3a9096d208 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 18 Nov 2025 15:47:16 +0100 Subject: [PATCH 03/40] changes from main in ci/ folder --- ci/basop-pages/basop_index.html | 6 +- ci/remove_unsupported_testcases.py | 92 ------------------------------ ci/setup_pages.py | 6 +- 3 files changed, 3 insertions(+), 101 deletions(-) delete mode 100644 ci/remove_unsupported_testcases.py diff --git a/ci/basop-pages/basop_index.html b/ci/basop-pages/basop_index.html index 5790b6f594..77f1220cfa 100644 --- a/ci/basop-pages/basop_index.html +++ b/ci/basop-pages/basop_index.html @@ -7,9 +7,7 @@

Regression tracking

- +
  • Long term regression
  • Daily long testvector tests

    @@ -29,5 +27,5 @@

    Test Coverage

    {} - + diff --git a/ci/remove_unsupported_testcases.py b/ci/remove_unsupported_testcases.py deleted file mode 100644 index 611452a89e..0000000000 --- a/ci/remove_unsupported_testcases.py +++ /dev/null @@ -1,92 +0,0 @@ -__copyright__ = """ -(C) 2022-2025 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. -""" - -from pathlib import Path -import argparse - -# Enter tag of testcases to remove here WITHOUT the leading // -TESTCASES = [ - # self_test.prm - # currently still crashing on BASOP main only, ivas-float-update is fine - "Multi-channel 7_1_4 bitrate switching, 48kHz in, 48kHz out, BINAURAL out, HR, JBM Prof 5", - "OMASA 2Dir2TC 4ISM at br sw techs 13.2 to 512 kbps start 80 kbps, 48kHz in, 48kHz out, EXT out", - "OMASA vdir2TC 3ISM at br sw techs 13.2 to 512 kbps start 160 kbps, 48kHz in, 48kHz out, MONO out, JBM Prof 5", - # self_test_ltv.prm - # rtpdump tests (also skipped in pytest code, but for documentation purposes added here as well) - "stereo bitrate switching from 13.2 kbps to 128 kbps, 48kHz in, 48kHz out, DTX on, EXT out, rtpdump", - "4 ISM with metadata bitrate switching from 24.4 kbps to 512 kbps, 48 kHz in, 48 kHz out, DTX on, BINAURAL out, rtpdump, PI data", - "SBA FOA bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data", - "MASA 2dir 2TC bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data", - "Multi-channel 5_1 bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", - "Stereo downmix to bit-exact EVS at 24400 kbps, 48kHz in, 48kHz out, rtpdump", - "EVS at 13.2 kbps, 48kHz in, 48kHz out, STEREO out, rtpdump", - "OMASA 2Dir2TC 3ISM at br sw techs 13.2 to 512 kbps start 160 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", - "OSBA 2ISM 2OA at bitrate switching 13.2 to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", - "OMASA vdir2TC 3ISM at br sw techs 13.2 to 512 kbps start 160 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", - "OMASA vdir2TC 4ISM at br sw techs 13.2 to 512 kbps start 80 kbps, 48kHz in, 48kHz out, HOA3 out", - "OMASA vdir1TC 3ISM at br sw techs 13.2 to 512 kbps start 48 kbps, 48kHz in, 32kHz out, STEREO out, JBM Prof 5", - "OMASA vdir2TC 4ISM at br sw techs 13.2 to 512 kbps start 80 kbps, 48kHz in, 48kHz out, EXT out", - "OMASA vdir2TC 4ISM at br sw techs 13.2 to 512 kbps start 80 kbps, 48kHz in, 48kHz out, FOA out, JBM Prof 5", - "OMASA vdir1TC 3ISM at br sw techs 13.2 to 512 kbps start 48 kbps, 48kHz in, 32kHz out, STEREO out, FER at 10%", -] - - - -def remove_testcases(cfg: Path, testcases: list): - """ - Go through file line by line and copy all testcases except the given ones - """ - with open(cfg, "r") as f: - content_in = f.readlines() - - content_out = list() - copy_flag = True - for line in content_in: - if any([tc in line for tc in testcases]): - copy_flag = False - - if copy_flag: - content_out.append(line) - elif line == "\n": - copy_flag = True - - with open(cfg, "w") as f: - f.write("".join(content_out)) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("cfg_files", nargs="+", type=Path) - args = parser.parse_args() - - testcases = TESTCASES - - for f in args.cfg_files: - remove_testcases(f, testcases) diff --git a/ci/setup_pages.py b/ci/setup_pages.py index b0251a264f..947526c78d 100755 --- a/ci/setup_pages.py +++ b/ci/setup_pages.py @@ -71,7 +71,7 @@ JOBS_BASOP_REPO = { "complexity-osba-in-binaural_room_ir-out": "OSBA in, BINAURAL_ROOM_IR out", "complexity-stereo-in-stereo-out": "Stereo in, Stereo out", # "timeless" jobs (not complexity) - "coverage-test-on-main-scheduled": "Coverage report", + "ivas-conformance-linux": "Conformance test coverage", "ivas-long-term-job-logs-overview": "Long term logs", } @@ -81,13 +81,10 @@ JOBS_FOR_PROJECT_ID = { } ARTIFACT_FOLDER_4_COVERAGE_JOBS = { - # for float "coverage-test-on-main-scheduled-stv": "coverage_stv", "coverage-test-on-main-scheduled-ltv": "coverage_ltv", "ivas-conformance-linux": "coverage_conformance", "coverage-merge": "coverage-merged", - # for BASOP - "coverage-test-on-main-scheduled": "coverage_enc_dec_rend", } ARTIFACTS = "artifacts.zip" @@ -146,7 +143,6 @@ def create_landing_page(jobs, index_html, project_id): link_html_complexity_text = "\n".join(link_html_complexity) link_html_coverage_text = "\n".join(link_html_coverage) - index_pages_tmpl = index_pages_tmpl.format( link_html_complexity_text, link_html_coverage_text ) -- GitLab From e3c9e81f54b1bf59582445235f536397ef2da389 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 18 Nov 2025 16:05:53 +0100 Subject: [PATCH 04/40] copy tests/ folder from main - to be reviewed and sorted out --- tests/conformance-test/test_26252.py | 45 +- tests/conftest.py | 18 +- tests/renderer/constants.py | 192 ++-- tests/renderer/test_renderer.py | 526 +++++++++-- tests/renderer/utils.py | 74 +- tests/renderer_short/constants.py | 196 +++-- tests/renderer_short/test_renderer.py | 828 ++++++++++++++++-- tests/renderer_short/utils.py | 79 +- tests/rtp/ivasrtp.py | 269 +++++- tests/rtp/test_format_switching.py | 169 ++++ tests/rtp/test_rtp.py | 151 +++- tests/split_rendering/constants.py | 141 +-- tests/split_rendering/test_split_rendering.py | 449 ++++++---- .../test_voip_be_splitrend_vs_binaural.py | 154 ++++ tests/split_rendering/utils.py | 224 ++--- tests/test_be_for_jbm_neutral_dly_profile.py | 49 +- tests/test_enc_passthrough.py | 2 + 17 files changed, 2730 insertions(+), 836 deletions(-) create mode 100644 tests/rtp/test_format_switching.py create mode 100644 tests/split_rendering/test_voip_be_splitrend_vs_binaural.py diff --git a/tests/conformance-test/test_26252.py b/tests/conformance-test/test_26252.py index 0179f58bfc..183f01e2ea 100644 --- a/tests/conformance-test/test_26252.py +++ b/tests/conformance-test/test_26252.py @@ -59,7 +59,7 @@ def replace_paths(instr, testv_path, ref_path, cut_path): test_dict = {} TEST_DIR = "." -scripts=["Readme_IVAS_enc.txt", "Readme_IVAS_dec.txt", "Readme_IVAS_rend.txt", "Readme_IVAS_JBM_dec.txt", "Readme_IVAS_ISAR_dec.txt", "Readme_IVAS_ISAR_post_rend.txt"] +scripts=["Readme_IVAS_enc.txt", "Readme_IVAS_dec.txt", "Readme_IVAS_rend.txt", "Readme_IVAS_JBM_dec.txt", "Readme_IVAS_ISAR_dec.txt", "Readme_IVAS_ISAR_post_rend.txt"] for s in scripts: with open(os.path.join(TEST_DIR, s), "r", encoding="UTF-8") as fp: @@ -125,12 +125,45 @@ def test_26252(test_tag, encoder_path, decoder_path, renderer_path, isar_post_re subprocess.run([renderer_path] + rend_opts.split()[1:], check = True) if isar_post_rend_opts: isar_post_rend_opts = replace_paths(isar_post_rend_opts, testv_path, ref_path, cut_path) - subprocess.run([isar_post_renderer_path] + isar_post_rend_opts.split()[1:], check = True) + subprocess.run([isar_post_renderer_path] + isar_post_rend_opts.split()[1:], check = True) diff_opts = replace_paths(diff_opts, testv_path, ref_path, cut_path) - result = True + + diff_in = {".wav": False, ".met": False, ".csv": False, ".192": False, ".bit": False} + for cmd in diff_opts.split(';'): - result = result and filecmp.cmp(cmd.split()[1], cmd.split()[2]) - if not result: - assert False, "Output differs" + file_a = Path(cmd.split()[1]) + file_b = Path(cmd.split()[2]) + + suffix = file_a.suffix + assert suffix == file_b.suffix + assert suffix in diff_in + + # for .csv ISM metadata files, do text-based comparison to not take line endings into account + # everything else (.wav output files and MASA metadata files) is compared as binary files + if suffix == ".csv": + with open(file_a, "r") as f_a: + with open(file_b, "r") as f_b: + a_content = f_a.read() + b_content = f_b.read() + + files_equal = a_content == b_content + else: + files_equal = filecmp.cmp(file_a, file_b) + + if not files_equal: + diff_in[suffix] = True + + if any(diff_in.values()): + result_str = "Output differs in: " + if diff_in[".csv"]: + result_str += "object metadata " + if diff_in[".met"]: + result_str += "MASA metadata " + if diff_in[".wav"]: + result_str += "waveform" + if diff_in[".192"] or diff_in[".bit"]: + result_str += "bitstream" + + pytest.fail(result_str) diff --git a/tests/conftest.py b/tests/conftest.py index 6f293b9502..9e2948ca23 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -570,6 +570,7 @@ class EncoderFrontend: run_dir: Optional[Path] = None, stats_file: Optional[Path] = None, compare_enc_dmx: Optional[bool] = False, + fmtsw_command: Optional[bool] = False, ) -> None: command = [str(self._path)] @@ -593,12 +594,17 @@ class EncoderFrontend: command.extend(add_option_list) # add mandatory parameters - command += [ - str(bitrate), - str(input_sampling_rate), - str(input_path), - str(output_bitstream_path), - ] + if fmtsw_command: + command += [ + str(input_path), + ] + else: + command += [ + str(bitrate), + str(input_sampling_rate), + str(input_path), + str(output_bitstream_path), + ] cmd_str = textwrap.indent(" ".join(command), prefix="\t") log_dbg_msg(f"{self._type} encoder command:\n{cmd_str}") diff --git a/tests/renderer/constants.py b/tests/renderer/constants.py index 766e64722b..387ab508db 100644 --- a/tests/renderer/constants.py +++ b/tests/renderer/constants.py @@ -30,8 +30,8 @@ accordance with the laws of the Federal Republic of Germany excluding its confli the United Nations Convention on Contracts on the International Sales of Goods. """ -from pathlib import Path import platform +from pathlib import Path """ Set up paths """ TESTS_DIR = Path(__file__).parent @@ -55,6 +55,8 @@ elif platform.system() in ["Linux", "Darwin"]: else: assert False, f"Unsupported platform {platform.system()}" +SAMPLING_RATES = ["48kHz", "32kHz", "16kHz"] + """ Renderer commandline template """ RENDERER_CMD = [ str(TESTS_DIR.parent.parent.joinpath("IVAS_rend")), @@ -115,27 +117,27 @@ FORMAT_TO_FILE_SMOKETEST = { "NDP_ISM4": NCHAN_TO_FILE[4], "MASA1": NCHAN_TO_FILE[1], "MASA2": NCHAN_TO_FILE[2], - "OMASA_1_1": NCHAN_TO_FILE[2], - "OMASA_1_2": NCHAN_TO_FILE[3], - "OMASA_1_3": NCHAN_TO_FILE[4], - "OMASA_1_4": NCHAN_TO_FILE[5], - "OMASA_2_1": NCHAN_TO_FILE[3], - "OMASA_2_2": NCHAN_TO_FILE[4], - "OMASA_2_3": NCHAN_TO_FILE[5], - "OMASA_2_4": NCHAN_TO_FILE[6], - "OSBA_1_1": NCHAN_TO_FILE[5], - "OSBA_2_1": NCHAN_TO_FILE[6], - "OSBA_3_1": NCHAN_TO_FILE[7], - "OSBA_4_1": NCHAN_TO_FILE[8], - "OSBA_1_2": NCHAN_TO_FILE[10], - "OSBA_2_2": NCHAN_TO_FILE[11], - "OSBA_3_2": NCHAN_TO_FILE[12], - "OSBA_4_2": NCHAN_TO_FILE[13], - "OSBA_1_3": NCHAN_TO_FILE[17], - "OSBA_2_3": NCHAN_TO_FILE[18], - "OSBA_3_3": NCHAN_TO_FILE[19], - "OSBA_4_3": NCHAN_TO_FILE[20], - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": NCHAN_TO_FILE[2], + "ISM2MASA1": NCHAN_TO_FILE[3], + "ISM3MASA1": NCHAN_TO_FILE[4], + "ISM4MASA1": NCHAN_TO_FILE[5], + "ISM1MASA2": NCHAN_TO_FILE[3], + "ISM2MASA2": NCHAN_TO_FILE[4], + "ISM3MASA2": NCHAN_TO_FILE[5], + "ISM4MASA2": NCHAN_TO_FILE[6], + "ISM1SBA1": NCHAN_TO_FILE[5], + "ISM2SBA1": NCHAN_TO_FILE[6], + "ISM3SBA1": NCHAN_TO_FILE[7], + "ISM4SBA1": NCHAN_TO_FILE[8], + "ISM1SBA2": NCHAN_TO_FILE[10], + "ISM2SBA2": NCHAN_TO_FILE[11], + "ISM3SBA2": NCHAN_TO_FILE[12], + "ISM4SBA2": NCHAN_TO_FILE[13], + "ISM1SBA3": NCHAN_TO_FILE[17], + "ISM2SBA3": NCHAN_TO_FILE[18], + "ISM3SBA3": NCHAN_TO_FILE[19], + "ISM4SBA3": NCHAN_TO_FILE[20], + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": NCHAN_TO_FILE[16], "4d4": NCHAN_TO_FILE[8], "t_design_4": NCHAN_TO_FILE[12], @@ -158,27 +160,27 @@ FORMAT_TO_FILE_COMPARETEST = { "ISM4": TESTV_DIR.joinpath("stv4ISM48s.wav"), "MASA1": TESTV_DIR.joinpath("stv1MASA1TC48c.wav"), "MASA2": TESTV_DIR.joinpath("stv2MASA2TC48c.wav"), - "OMASA_1_1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.wav"), - "OMASA_1_2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.wav"), - "OMASA_1_3": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.wav"), - "OMASA_1_4": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.wav"), - "OMASA_2_1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.wav"), - "OMASA_2_2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.wav"), - "OMASA_2_3": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.wav"), - "OMASA_2_4": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA2TC48c.wav"), - "OSBA_1_1": TESTV_DIR.joinpath("stvOSBA_1ISM_FOA48c.wav"), - "OSBA_1_2": TESTV_DIR.joinpath("stvOSBA_1ISM_2OA48c.wav"), - "OSBA_1_3": TESTV_DIR.joinpath("stvOSBA_1ISM_3OA48c.wav"), - "OSBA_2_1": TESTV_DIR.joinpath("stvOSBA_2ISM_FOA48c.wav"), - "OSBA_2_2": TESTV_DIR.joinpath("stvOSBA_2ISM_2OA48c.wav"), - "OSBA_2_3": TESTV_DIR.joinpath("stvOSBA_2ISM_3OA48c.wav"), - "OSBA_3_1": TESTV_DIR.joinpath("stvOSBA_3ISM_FOA48c.wav"), - "OSBA_3_2": TESTV_DIR.joinpath("stvOSBA_3ISM_2OA48c.wav"), - "OSBA_3_3": TESTV_DIR.joinpath("stvOSBA_3ISM_3OA48c.wav"), - "OSBA_4_1": TESTV_DIR.joinpath("stvOSBA_4ISM_FOA48c.wav"), - "OSBA_4_2": TESTV_DIR.joinpath("stvOSBA_4ISM_2OA48c.wav"), - "OSBA_4_3": TESTV_DIR.joinpath("stvOSBA_4ISM_3OA48c.wav"), - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.wav"), + "ISM2MASA1": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.wav"), + "ISM3MASA1": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.wav"), + "ISM4MASA1": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.wav"), + "ISM1MASA2": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.wav"), + "ISM2MASA2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.wav"), + "ISM3MASA2": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.wav"), + "ISM4MASA2": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA2TC48c.wav"), + "ISM1SBA1": TESTV_DIR.joinpath("stvOSBA_1ISM_FOA48c.wav"), + "ISM1SBA2": TESTV_DIR.joinpath("stvOSBA_1ISM_2OA48c.wav"), + "ISM1SBA3": TESTV_DIR.joinpath("stvOSBA_1ISM_3OA48c.wav"), + "ISM2SBA1": TESTV_DIR.joinpath("stvOSBA_2ISM_FOA48c.wav"), + "ISM2SBA2": TESTV_DIR.joinpath("stvOSBA_2ISM_2OA48c.wav"), + "ISM2SBA3": TESTV_DIR.joinpath("stvOSBA_2ISM_3OA48c.wav"), + "ISM3SBA1": TESTV_DIR.joinpath("stvOSBA_3ISM_FOA48c.wav"), + "ISM3SBA2": TESTV_DIR.joinpath("stvOSBA_3ISM_2OA48c.wav"), + "ISM3SBA3": TESTV_DIR.joinpath("stvOSBA_3ISM_3OA48c.wav"), + "ISM4SBA1": TESTV_DIR.joinpath("stvOSBA_4ISM_FOA48c.wav"), + "ISM4SBA2": TESTV_DIR.joinpath("stvOSBA_4ISM_2OA48c.wav"), + "ISM4SBA3": TESTV_DIR.joinpath("stvOSBA_4ISM_3OA48c.wav"), + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": TESTV_DIR.joinpath("stv3OA48c.wav"), "4d4": TESTV_DIR.joinpath("stv71MC48c.wav"), "t_design_4": TESTV_DIR.joinpath("stv714MC48c.wav"), @@ -201,27 +203,27 @@ FORMAT_TO_FILE_LTV = { "ISM4": LTV_DIR.joinpath("ltv48_4ISM.wav"), "MASA1": LTV_DIR.joinpath("ltv48_MASA1TC.wav"), "MASA2": LTV_DIR.joinpath("ltv48_MASA2TC.wav"), - "OMASA_1_1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.wav"), - "OMASA_1_2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.wav"), - "OMASA_1_3": LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.wav"), - "OMASA_1_4": LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.wav"), - "OMASA_2_1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.wav"), - "OMASA_2_2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.wav"), - "OMASA_2_3": LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.wav"), - "OMASA_2_4": LTV_DIR.joinpath("ltv48_OMASA_4ISM_2TC.wav"), - "OSBA_1_1": LTV_DIR.joinpath("ltv48_OSBA_1ISM_FOA.wav"), - "OSBA_1_2": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA2.wav"), - "OSBA_1_3": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA3.wav"), - "OSBA_2_1": LTV_DIR.joinpath("ltv48_OSBA_2ISM_FOA.wav"), - "OSBA_2_2": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA2.wav"), - "OSBA_2_3": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA3.wav"), - "OSBA_3_1": LTV_DIR.joinpath("ltv48_OSBA_3ISM_FOA.wav"), - "OSBA_3_2": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA2.wav"), - "OSBA_3_3": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA3.wav"), - "OSBA_4_1": LTV_DIR.joinpath("ltv48_OSBA_4ISM_FOA.wav"), - "OSBA_4_2": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA2.wav"), - "OSBA_4_3": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA3.wav"), - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.wav"), + "ISM2MASA1": LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.wav"), + "ISM3MASA1": LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.wav"), + "ISM4MASA1": LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.wav"), + "ISM1MASA2": LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.wav"), + "ISM2MASA2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.wav"), + "ISM3MASA2": LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.wav"), + "ISM4MASA2": LTV_DIR.joinpath("ltv48_OMASA_4ISM_2TC.wav"), + "ISM1SBA1": LTV_DIR.joinpath("ltv48_OSBA_1ISM_FOA.wav"), + "ISM1SBA2": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA2.wav"), + "ISM1SBA3": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA3.wav"), + "ISM2SBA1": LTV_DIR.joinpath("ltv48_OSBA_2ISM_FOA.wav"), + "ISM2SBA2": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA2.wav"), + "ISM2SBA3": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA3.wav"), + "ISM3SBA1": LTV_DIR.joinpath("ltv48_OSBA_3ISM_FOA.wav"), + "ISM3SBA2": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA2.wav"), + "ISM3SBA3": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA3.wav"), + "ISM4SBA1": LTV_DIR.joinpath("ltv48_OSBA_4ISM_FOA.wav"), + "ISM4SBA2": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA2.wav"), + "ISM4SBA3": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA3.wav"), + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": LTV_DIR.joinpath("ltv48_HOA3.wav"), "4d4": LTV_DIR.joinpath("ltv48_MC71.wav"), "t_design_4": LTV_DIR.joinpath("ltv48_MC714.wav"), @@ -252,44 +254,44 @@ FORMAT_TO_METADATA_FILES = { ], "MASA1": [str(TESTV_DIR.joinpath("stv1MASA1TC48c.met"))], "MASA2": [str(TESTV_DIR.joinpath("stv2MASA2TC48c.met"))], - "OMASA_1_1": [ + "ISM1MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.met")), ], - "OMASA_1_2": [ + "ISM2MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.met")), ], - "OMASA_1_3": [ + "ISM3MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.met")), ], - "OMASA_1_4": [ + "ISM4MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvISM4.csv")), str(TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.met")), ], - "OMASA_2_1": [ + "ISM1MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.met")), ], - "OMASA_2_2": [ + "ISM2MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.met")), ], - "OMASA_2_3": [ + "ISM3MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.met")), ], - "OMASA_2_4": [ + "ISM4MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), @@ -315,7 +317,7 @@ FORMAT_TO_METADATA_FILES_LTV = { str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltvISM4.csv")), ], - "NDP_ISM4": [ # Should not be needed, because it is included in all ISM metadata files. + "NDP_ISM4": [ # Should not be needed, because it is included in all ISM metadata files. str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), @@ -323,44 +325,44 @@ FORMAT_TO_METADATA_FILES_LTV = { ], "MASA1": [str(LTV_DIR.joinpath("ltv48_MASA1TC.met"))], "MASA2": [str(LTV_DIR.joinpath("ltv48_MASA2TC.met"))], - "OMASA_1_1": [ + "ISM1MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.met")), ], - "OMASA_1_2": [ + "ISM2MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.met")), ], - "OMASA_1_3": [ + "ISM3MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.met")), ], - "OMASA_1_4": [ + "ISM4MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltvISM4.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.met")), ], - "OMASA_2_1": [ + "ISM1MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.met")), ], - "OMASA_2_2": [ + "ISM2MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.met")), ], - "OMASA_2_3": [ + "ISM3MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.met")), ], - "OMASA_2_4": [ + "ISM4MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), @@ -374,6 +376,31 @@ INPUT_FORMATS_AMBI = ["FOA", "HOA2", "HOA3"] INPUT_FORMATS_MC = ["MONO", "STEREO", "5_1", "5_1_2", "5_1_4", "7_1", "7_1_4"] INPUT_FORMATS_ISM = ["ISM1", "ISM2", "ISM3", "ISM4"] INPUT_FORMATS_MASA = ["MASA1", "MASA2"] +INPUT_FORMATS_OMASA = [ + "ISM1MASA1", + "ISM2MASA1", + "ISM3MASA1", + "ISM4MASA1", + "ISM1MASA2", + "ISM2MASA2", + "ISM3MASA2", + "ISM4MASA2", +] +INPUT_FORMATS_OSBA = [ + "ISM1SBA1", + "ISM1SBA2", + "ISM1SBA3", + "ISM2SBA1", + "ISM2SBA2", + "ISM2SBA3", + "ISM3SBA1", + "ISM3SBA2", + "ISM3SBA3", + "ISM4SBA1", + "ISM4SBA2", + "ISM4SBA3", +] + """ Non binaural / parametric output formats """ OUTPUT_FORMATS = [ @@ -399,7 +426,7 @@ CUSTOM_LS_TO_TEST = [ """ Mixed scene ( metadata ) rendering """ METADATA_SCENES_TO_TEST = ["mixed_scene", "mixed_scene_simple"] METADATA_SCENES_TO_TEST_NO_BE = ["masa_scene"] -METADATA_SCENES_TO_TEST_MASA_PREREND = ["mixed_mc714_foa_masa2_ism4"] +METADATA_SCENES_TO_TEST_MASA_PREREND = ["mixed_mc714_foa_masa2_ism4", "mixed_10sec_mc714_foa_masa1_ism4", "mixed_10sec_mc714_foa_masa2_ism4"] """ Binaural rendering """ OUTPUT_FORMATS_BINAURAL = ["BINAURAL", "BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"] @@ -418,4 +445,3 @@ PEAQ_SUPPORTED_FMT = [ "BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB", ] - diff --git a/tests/renderer/test_renderer.py b/tests/renderer/test_renderer.py index 8cfda2f6db..d12cb47b19 100644 --- a/tests/renderer/test_renderer.py +++ b/tests/renderer/test_renderer.py @@ -33,25 +33,26 @@ the United Nations Convention on Contracts on the International Sales of Goods. import pytest from .constants import ( - FORMAT_TO_METADATA_FILES_LTV, - OUTPUT_FORMATS, - INPUT_FORMATS_AMBI, - FRAMING_TO_TEST, + CUSTOM_LAYOUT_DIR, + CUSTOM_LS_TO_TEST, EXE_SUFFIX, - OUTPUT_FORMATS_BINAURAL, + FRAMING_TO_TEST, HR_TRAJECTORIES_TO_TEST, HR_TRAJECTORY_DIR, - INPUT_FORMATS_MC, + INPUT_FORMATS_AMBI, INPUT_FORMATS_ISM, INPUT_FORMATS_MASA, + INPUT_FORMATS_MC, + INPUT_FORMATS_OMASA, + INPUT_FORMATS_OSBA, + METADATA_SCENES_TO_TEST, METADATA_SCENES_TO_TEST_MASA_PREREND, + OUTPUT_FORMATS, + OUTPUT_FORMATS_BINAURAL, + SAMPLING_RATES, TEST_VECTOR_DIR, - CUSTOM_LS_TO_TEST, - CUSTOM_LAYOUT_DIR, - METADATA_SCENES_TO_TEST, ) -from .utils import run_renderer, compare_renderer_args -from ..conftest import props_to_record +from .utils import compare_renderer_args, run_renderer ############################################################################## # Bit-exactness tests @@ -65,6 +66,7 @@ from ..conftest import props_to_record @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics( record_property, props_to_record, @@ -72,6 +74,7 @@ def test_ambisonics( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -93,12 +96,14 @@ def test_ambisonics( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics_binaural_static( record_property, props_to_record, @@ -106,6 +111,7 @@ def test_ambisonics_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -127,6 +133,7 @@ def test_ambisonics_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -134,6 +141,7 @@ def test_ambisonics_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics_binaural_headrotation( record_property, props_to_record, @@ -142,6 +150,7 @@ def test_ambisonics_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -164,14 +173,25 @@ def test_ambisonics_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.skip(reason="Not supported for BASOP code currently") @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[2:]) -@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize( + "in_fmt", + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], +) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) @pytest.mark.parametrize("aeid", ["1", "0"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_dynamic_acoustic_environment( record_property, props_to_record, @@ -179,6 +199,7 @@ def test_dynamic_acoustic_environment( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -187,8 +208,11 @@ def test_dynamic_acoustic_environment( aeid, split_comparison, ): - rend_config_path = TEST_VECTOR_DIR.joinpath(f"rend_config_combined.cfg") - rend_config_path.with_stem(f"rend_config") + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + rend_config_path = TEST_VECTOR_DIR.joinpath("rend_config_combined.cfg") + rend_config_path.with_stem("rend_config") run_renderer( record_property, @@ -206,13 +230,24 @@ def test_dynamic_acoustic_environment( config_file=rend_config_path, aeid=aeid, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.skip("MSAN errors in BASOP need to be fixed") @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[2:]) -@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize( + "in_fmt", + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], +) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_dynamic_acoustic_environment_file( record_property, props_to_record, @@ -220,6 +255,7 @@ def test_dynamic_acoustic_environment_file( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -227,10 +263,13 @@ def test_dynamic_acoustic_environment_file( get_odg_bin, split_comparison, ): - rend_config_path = TEST_VECTOR_DIR.joinpath(f"rend_config_combined.cfg") - rend_config_path.with_stem(f"rend_config") + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + rend_config_path = TEST_VECTOR_DIR.joinpath("rend_config_combined.cfg") + rend_config_path.with_stem("rend_config") - aeid = TEST_VECTOR_DIR.joinpath(f"aeid1.txt") + aeid = TEST_VECTOR_DIR.joinpath("aeid1.txt") run_renderer( record_property, @@ -248,6 +287,7 @@ def test_dynamic_acoustic_environment_file( config_file=rend_config_path, aeid=aeid, split_comparison=split_comparison, + sr=fs, ) @@ -257,6 +297,7 @@ def test_dynamic_acoustic_environment_file( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel( record_property, props_to_record, @@ -264,6 +305,7 @@ def test_multichannel( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -285,12 +327,14 @@ def test_multichannel( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel_binaural_static( record_property, props_to_record, @@ -298,6 +342,7 @@ def test_multichannel_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -322,6 +367,7 @@ def test_multichannel_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -329,6 +375,7 @@ def test_multichannel_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel_binaural_headrotation( record_property, props_to_record, @@ -337,6 +384,7 @@ def test_multichannel_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -362,6 +410,7 @@ def test_multichannel_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -371,6 +420,7 @@ def test_multichannel_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism( record_property, props_to_record, @@ -378,6 +428,7 @@ def test_ism( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -399,12 +450,14 @@ def test_ism( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism_binaural_static( record_property, props_to_record, @@ -412,6 +465,7 @@ def test_ism_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -433,6 +487,7 @@ def test_ism_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -440,6 +495,7 @@ def test_ism_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism_binaural_headrotation( record_property, props_to_record, @@ -448,6 +504,7 @@ def test_ism_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -470,6 +527,7 @@ def test_ism_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -479,6 +537,7 @@ def test_ism_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa( record_property, props_to_record, @@ -486,6 +545,7 @@ def test_masa( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -507,12 +567,14 @@ def test_masa( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_binaural_static( record_property, props_to_record, @@ -520,6 +582,7 @@ def test_masa_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -527,9 +590,6 @@ def test_masa_binaural_static( get_odg_bin, split_comparison, ): - if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: - pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") - run_renderer( record_property, props_to_record, @@ -544,6 +604,7 @@ def test_masa_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -551,6 +612,7 @@ def test_masa_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_binaural_headrotation( record_property, props_to_record, @@ -559,6 +621,7 @@ def test_masa_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -566,9 +629,6 @@ def test_masa_binaural_headrotation( get_odg_bin, split_comparison, ): - if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: - pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") - run_renderer( record_property, props_to_record, @@ -584,15 +644,54 @@ def test_masa_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST_MASA_PREREND) +@pytest.mark.parametrize("out_fmt", INPUT_FORMATS_MASA) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_prerend( record_property, props_to_record, test_info, in_fmt, + out_fmt, + fs, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", INPUT_FORMATS_MASA) +@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST_MASA_PREREND) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_masa_prerend_scenes( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -605,15 +704,250 @@ def test_masa_prerend( props_to_record, test_info, "META", - "MASA2", - metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + out_fmt, + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}_{fs[:2]}.txt"), + binary_suffix=EXE_SUFFIX, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +""" OMASA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa_binaural_static( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa_binaural_headrotation( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + trj_file, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +""" OSBA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba_binaural_static( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba_binaural_headrotation( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + trj_file, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), binary_suffix=EXE_SUFFIX, + frame_size=frame_size, get_mld=get_mld, mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -623,6 +957,7 @@ def test_masa_prerend( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input( record_property, props_to_record, @@ -630,6 +965,7 @@ def test_custom_ls_input( in_layout, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -651,20 +987,30 @@ def test_custom_ls_input( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize( "in_fmt", - [*INPUT_FORMATS_AMBI, *INPUT_FORMATS_MC, *INPUT_FORMATS_ISM, *INPUT_FORMATS_MASA], + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], ) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_output( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -672,11 +1018,6 @@ def test_custom_ls_output( get_odg_bin, split_comparison, ): - # TODO: revert once BASOP is brought up-to-date - if in_fmt in INPUT_FORMATS_MASA: - pytest.skip("MASA to custom LS not supported on ivas-float-update yet") - if in_fmt in INPUT_FORMATS_ISM and out_fmt == "t_design_4": - pytest.skip("ISMx + t_design_4 skipped until bug in BASOP is fixed") run_renderer( record_property, props_to_record, @@ -690,17 +1031,20 @@ def test_custom_ls_output( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize("in_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_output( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -721,12 +1065,14 @@ def test_custom_ls_input_output( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @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) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_binaural( record_property, props_to_record, @@ -734,6 +1080,7 @@ def test_custom_ls_input_binaural( in_layout, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -755,6 +1102,7 @@ def test_custom_ls_input_binaural( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -762,6 +1110,7 @@ def test_custom_ls_input_binaural( @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) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_binaural_headrotation( record_property, props_to_record, @@ -770,6 +1119,7 @@ def test_custom_ls_input_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -792,6 +1142,7 @@ def test_custom_ls_input_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -801,6 +1152,7 @@ def test_custom_ls_input_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_metadata( record_property, props_to_record, @@ -808,6 +1160,7 @@ def test_metadata( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -821,7 +1174,7 @@ def test_metadata( test_info, "META", out_fmt, - metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}_{fs[:2]}.txt"), binary_suffix=EXE_SUFFIX, frame_size=frame_size, get_mld=get_mld, @@ -830,6 +1183,7 @@ def test_metadata( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -839,6 +1193,7 @@ def test_metadata( @pytest.mark.parametrize("out_fmt", ["STEREO"]) @pytest.mark.parametrize("in_fmt", ["MONO"]) @pytest.mark.parametrize("non_diegetic_pan", ["0", "-30", "45", "90", "-90"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_non_diegetic_pan_static( record_property, props_to_record, @@ -846,6 +1201,7 @@ def test_non_diegetic_pan_static( in_fmt, out_fmt, non_diegetic_pan, + fs, get_mld, get_mld_lim, get_ssnr, @@ -867,18 +1223,21 @@ def test_non_diegetic_pan_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", ["STEREO"]) @pytest.mark.parametrize("in_fmt", ["ISM1"]) @pytest.mark.parametrize("non_diegetic_pan", ["0", "-30", "45", "90", "-90"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_non_diegetic_pan_ism_static( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, non_diegetic_pan, get_mld, get_mld_lim, @@ -901,6 +1260,7 @@ def test_non_diegetic_pan_ism_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -1044,31 +1404,27 @@ def test_ambisonics_binaural_headrotation_refvecequal( if test_info.config.option.create_ref or test_info.config.option.create_cut: pytest.skip("OTR tests only run for smoke test") - # TODO revert - if in_fmt == "HOA3" and out_fmt == "BINAURAL_ROOM_REVERB": - pytest.xfail("WIP : minor differences to be resolved") - else: - compare_renderer_args( - record_property, - props_to_record, - test_info, - in_fmt, - out_fmt, - ref_kwargs={ - "name_extension": "refvecequal", - "frame_size": "5", - }, - cut_kwargs={ - "trj_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s.csv" - ), - "refvec_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s-Vector3.csv" - ), - "frame_size": "5", - }, - split_comparison=split_comparison, - ) + compare_renderer_args( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + ref_kwargs={ + "name_extension": "refvecequal", + "frame_size": "5", + }, + cut_kwargs={ + "trj_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s.csv" + ), + "refvec_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s-Vector3.csv" + ), + "frame_size": "5", + }, + split_comparison=split_comparison, + ) # This test compares rendering with: @@ -1088,32 +1444,28 @@ def test_ambisonics_binaural_headrotation_refvec_rotating( if test_info.config.option.create_ref or test_info.config.option.create_cut: pytest.skip("OTR tests only run for smoke test") - # TODO revert - if in_fmt == "HOA2" and out_fmt == "BINAURAL_ROOM_REVERB": - pytest.xfail("WIP : minor differences to be resolved") - else: - compare_renderer_args( - record_property, - props_to_record, - test_info, - in_fmt, - out_fmt, - ref_kwargs={ - "name_extension": "refvec_rotating", - "trj_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s.csv" - ), - "frame_size": "5", - }, - cut_kwargs={ - "trj_file": HR_TRAJECTORY_DIR.joinpath("const000.csv"), - "refvec_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s-ccw-Vector3.csv" - ), - "frame_size": "5", - }, - split_comparison=split_comparison, - ) + compare_renderer_args( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + ref_kwargs={ + "name_extension": "refvec_rotating", + "trj_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s.csv" + ), + "frame_size": "5", + }, + cut_kwargs={ + "trj_file": HR_TRAJECTORY_DIR.joinpath("const000.csv"), + "refvec_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s-ccw-Vector3.csv" + ), + "frame_size": "5", + }, + split_comparison=split_comparison, + ) # This test compares rendering with: diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py index f34512d965..1aa7370f08 100644 --- a/tests/renderer/utils.py +++ b/tests/renderer/utils.py @@ -30,44 +30,44 @@ accordance with the laws of the Federal Republic of Germany excluding its confli the United Nations Convention on Contracts on the International Sales of Goods. """ +import errno import filecmp import logging import os -from pathlib import Path +import re import subprocess as sp import sys +import tempfile +from pathlib import Path from typing import Dict, Optional, Union import numpy as np import pytest -import re -import errno -import tempfile +from ..constants import CAT_NORMAL from .compare_audio import compare_audio_arrays from .constants import ( - LTV_DIR, - SCRIPTS_DIR, - OUTPUT_PATH_REF, - OUTPUT_PATH_CUT, + BIN_SUFFIX_MERGETARGET, FORMAT_TO_FILE_COMPARETEST, FORMAT_TO_FILE_LTV, + FORMAT_TO_FILE_SMOKETEST, FORMAT_TO_METADATA_FILES, FORMAT_TO_METADATA_FILES_LTV, - FORMAT_TO_FILE_SMOKETEST, - RENDERER_CMD, - BIN_SUFFIX_MERGETARGET, + LTV_DIR, + OUTPUT_PATH_CUT, + OUTPUT_PATH_REF, PEAQ_SUPPORTED_FMT, + RENDERER_CMD, + SCRIPTS_DIR, ) -from ..constants import CAT_NORMAL sys.path.append(SCRIPTS_DIR) from pyaudio3dtools.audiofile import readfile -from ..cmp_pcm import cmp_pcm -from ..conftest import parse_properties, get_split_idx +from ..cmp_pcm import cmp_pcm +from ..conftest import get_split_idx, parse_properties -def _run_cmd(cmd, env, test_info=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 @@ -92,32 +92,30 @@ def _run_cmd(cmd, env, test_info=None): raise SystemError(error) -def run_cmd(cmd, test_info, env=None): +def run_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env, test_info) + _run_cmd(cmd, test_info=test_info, env=env) -def run_isar_ext_rend_cmd(cmd, env=None): +def run_isar_ext_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR EXT REND command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env) + _run_cmd(cmd, test_info=test_info, env=env) -def run_ivas_isar_enc_cmd(cmd, env=None): +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, env) + _run_cmd(cmd, test_info=test_info, env=env) - -def run_ivas_isar_dec_cmd(cmd, env=None): +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") else: logging.info(f"\nDUT decoder command:\n\t{' '.join(cmd)}\n") - _run_cmd(cmd, env) - + _run_cmd(cmd, test_info=test_info, env=env) -def run_isar_post_rend_cmd(cmd, env=None): +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, env) + _run_cmd(cmd, test_info=test_info, env=env) def check_BE( @@ -178,7 +176,7 @@ def run_renderer( aeid: Optional[Union[Path, int]] = None, in_file=None, out_file=None, - sr=48, + sr="48kHz", render_for_peaq=False, split_comparison=False, ) -> str: @@ -270,12 +268,20 @@ def run_renderer( else: in_file = format_to_file[in_fmt] in_name = in_fmt + in_file = str(in_file).replace("48", sr[:2]) if in_meta_files is None and in_fmt in format_to_metadata_files: in_meta_files = format_to_metadata_files[in_fmt] + # If metadata not given with ISM input, use default NULL + if in_meta_files is None and isinstance(in_fmt, str) and "ism" in in_fmt.lower(): + match = re.search(r"ism(\d)", in_fmt.lower()) + assert match is not None + num_obj = int(match[1]) + in_meta_files = ["NULL"] * num_obj + if out_file is None: - 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}{aeid_name}.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}{aeid_name}_{sr}.wav" out_file = str(output_path_base.joinpath(out_file_stem)) cmd = RENDERER_CMD[:] @@ -283,7 +289,7 @@ def run_renderer( cmd[4] = str(in_fmt) cmd[6] = str(out_file) cmd[8] = str(out_fmt) - cmd[10] = str(sr) + cmd[10] = str(sr[:2]) if test_info.config.option.create_ref: cmd[0] += BIN_SUFFIX_MERGETARGET @@ -373,7 +379,7 @@ def run_renderer( cmd2[4] = str(out_fmt) # in_fmt cmd2[6] = odg_test # out_file cmd2[8] = new_fmt # out_fmt - cmd2[10] = str(sr) + cmd2[10] = str(sr[:2]) cmd2[0] += BIN_SUFFIX_MERGETARGET # Use IVAS_rend_ref for re-rendering cmd2[0] += binary_suffix if "MASA" in str(out_fmt): @@ -554,8 +560,8 @@ def binauralize_input_and_output( # If extended metadata is not used, strip the metadata for the external renderer extended_md_used = ( re.search(r"-ism\s?\+[1-4]", enc_opts) - and not "OMASA" in in_fmt - and not "OSBA" in in_fmt + and "OMASA" not in in_fmt + and "OSBA" not in in_fmt ) if not extended_md_used and n_obj > 0: truncated_meta_files = [] @@ -637,7 +643,7 @@ def binauralize_input_and_output( aeid = findstr(r"-aeid\s+(\S+)", dec_opts) - if not output_config.upper() in PEAQ_SUPPORTED_FMT: + if output_config.upper() not in PEAQ_SUPPORTED_FMT: # Render output to BINAURAL output_reformat = "BINAURAL" diff --git a/tests/renderer_short/constants.py b/tests/renderer_short/constants.py index 766e64722b..1c9eabbd13 100644 --- a/tests/renderer_short/constants.py +++ b/tests/renderer_short/constants.py @@ -30,8 +30,8 @@ accordance with the laws of the Federal Republic of Germany excluding its confli the United Nations Convention on Contracts on the International Sales of Goods. """ -from pathlib import Path import platform +from pathlib import Path """ Set up paths """ TESTS_DIR = Path(__file__).parent @@ -55,6 +55,8 @@ elif platform.system() in ["Linux", "Darwin"]: else: assert False, f"Unsupported platform {platform.system()}" +SAMPLING_RATES = ["48kHz", "32kHz", "16kHz"] + """ Renderer commandline template """ RENDERER_CMD = [ str(TESTS_DIR.parent.parent.joinpath("IVAS_rend")), @@ -115,29 +117,28 @@ FORMAT_TO_FILE_SMOKETEST = { "NDP_ISM4": NCHAN_TO_FILE[4], "MASA1": NCHAN_TO_FILE[1], "MASA2": NCHAN_TO_FILE[2], - "OMASA_1_1": NCHAN_TO_FILE[2], - "OMASA_1_2": NCHAN_TO_FILE[3], - "OMASA_1_3": NCHAN_TO_FILE[4], - "OMASA_1_4": NCHAN_TO_FILE[5], - "OMASA_2_1": NCHAN_TO_FILE[3], - "OMASA_2_2": NCHAN_TO_FILE[4], - "OMASA_2_3": NCHAN_TO_FILE[5], - "OMASA_2_4": NCHAN_TO_FILE[6], - "OSBA_1_1": NCHAN_TO_FILE[5], - "OSBA_2_1": NCHAN_TO_FILE[6], - "OSBA_3_1": NCHAN_TO_FILE[7], - "OSBA_4_1": NCHAN_TO_FILE[8], - "OSBA_1_2": NCHAN_TO_FILE[10], - "OSBA_2_2": NCHAN_TO_FILE[11], - "OSBA_3_2": NCHAN_TO_FILE[12], - "OSBA_4_2": NCHAN_TO_FILE[13], - "OSBA_1_3": NCHAN_TO_FILE[17], - "OSBA_2_3": NCHAN_TO_FILE[18], - "OSBA_3_3": NCHAN_TO_FILE[19], - "OSBA_4_3": NCHAN_TO_FILE[20], - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": NCHAN_TO_FILE[2], + "ISM2MASA1": NCHAN_TO_FILE[3], + "ISM3MASA1": NCHAN_TO_FILE[4], + "ISM4MASA1": NCHAN_TO_FILE[5], + "ISM1MASA2": NCHAN_TO_FILE[3], + "ISM2MASA2": NCHAN_TO_FILE[4], + "ISM3MASA2": NCHAN_TO_FILE[5], + "ISM4MASA2": NCHAN_TO_FILE[6], + "ISM1SBA1": NCHAN_TO_FILE[5], + "ISM2SBA1": NCHAN_TO_FILE[6], + "ISM3SBA1": NCHAN_TO_FILE[7], + "ISM4SBA1": NCHAN_TO_FILE[8], + "ISM1SBA2": NCHAN_TO_FILE[10], + "ISM2SBA2": NCHAN_TO_FILE[11], + "ISM3SBA2": NCHAN_TO_FILE[12], + "ISM4SBA2": NCHAN_TO_FILE[13], + "ISM1SBA3": NCHAN_TO_FILE[17], + "ISM2SBA3": NCHAN_TO_FILE[18], + "ISM3SBA3": NCHAN_TO_FILE[19], + "ISM4SBA3": NCHAN_TO_FILE[20], + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": NCHAN_TO_FILE[16], - "4d4": NCHAN_TO_FILE[8], "t_design_4": NCHAN_TO_FILE[12], } @@ -158,29 +159,28 @@ FORMAT_TO_FILE_COMPARETEST = { "ISM4": TESTV_DIR.joinpath("stv4ISM48s.wav"), "MASA1": TESTV_DIR.joinpath("stv1MASA1TC48c.wav"), "MASA2": TESTV_DIR.joinpath("stv2MASA2TC48c.wav"), - "OMASA_1_1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.wav"), - "OMASA_1_2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.wav"), - "OMASA_1_3": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.wav"), - "OMASA_1_4": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.wav"), - "OMASA_2_1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.wav"), - "OMASA_2_2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.wav"), - "OMASA_2_3": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.wav"), - "OMASA_2_4": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA2TC48c.wav"), - "OSBA_1_1": TESTV_DIR.joinpath("stvOSBA_1ISM_FOA48c.wav"), - "OSBA_1_2": TESTV_DIR.joinpath("stvOSBA_1ISM_2OA48c.wav"), - "OSBA_1_3": TESTV_DIR.joinpath("stvOSBA_1ISM_3OA48c.wav"), - "OSBA_2_1": TESTV_DIR.joinpath("stvOSBA_2ISM_FOA48c.wav"), - "OSBA_2_2": TESTV_DIR.joinpath("stvOSBA_2ISM_2OA48c.wav"), - "OSBA_2_3": TESTV_DIR.joinpath("stvOSBA_2ISM_3OA48c.wav"), - "OSBA_3_1": TESTV_DIR.joinpath("stvOSBA_3ISM_FOA48c.wav"), - "OSBA_3_2": TESTV_DIR.joinpath("stvOSBA_3ISM_2OA48c.wav"), - "OSBA_3_3": TESTV_DIR.joinpath("stvOSBA_3ISM_3OA48c.wav"), - "OSBA_4_1": TESTV_DIR.joinpath("stvOSBA_4ISM_FOA48c.wav"), - "OSBA_4_2": TESTV_DIR.joinpath("stvOSBA_4ISM_2OA48c.wav"), - "OSBA_4_3": TESTV_DIR.joinpath("stvOSBA_4ISM_3OA48c.wav"), - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.wav"), + "ISM2MASA1": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.wav"), + "ISM3MASA1": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.wav"), + "ISM4MASA1": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.wav"), + "ISM1MASA2": TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.wav"), + "ISM2MASA2": TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.wav"), + "ISM3MASA2": TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.wav"), + "ISM4MASA2": TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA2TC48c.wav"), + "ISM1SBA1": TESTV_DIR.joinpath("stvOSBA_1ISM_FOA48c.wav"), + "ISM1SBA2": TESTV_DIR.joinpath("stvOSBA_1ISM_2OA48c.wav"), + "ISM1SBA3": TESTV_DIR.joinpath("stvOSBA_1ISM_3OA48c.wav"), + "ISM2SBA1": TESTV_DIR.joinpath("stvOSBA_2ISM_FOA48c.wav"), + "ISM2SBA2": TESTV_DIR.joinpath("stvOSBA_2ISM_2OA48c.wav"), + "ISM2SBA3": TESTV_DIR.joinpath("stvOSBA_2ISM_3OA48c.wav"), + "ISM3SBA1": TESTV_DIR.joinpath("stvOSBA_3ISM_FOA48c.wav"), + "ISM3SBA2": TESTV_DIR.joinpath("stvOSBA_3ISM_2OA48c.wav"), + "ISM3SBA3": TESTV_DIR.joinpath("stvOSBA_3ISM_3OA48c.wav"), + "ISM4SBA1": TESTV_DIR.joinpath("stvOSBA_4ISM_FOA48c.wav"), + "ISM4SBA2": TESTV_DIR.joinpath("stvOSBA_4ISM_2OA48c.wav"), + "ISM4SBA3": TESTV_DIR.joinpath("stvOSBA_4ISM_3OA48c.wav"), + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": TESTV_DIR.joinpath("stv3OA48c.wav"), - "4d4": TESTV_DIR.joinpath("stv71MC48c.wav"), "t_design_4": TESTV_DIR.joinpath("stv714MC48c.wav"), } @@ -201,29 +201,28 @@ FORMAT_TO_FILE_LTV = { "ISM4": LTV_DIR.joinpath("ltv48_4ISM.wav"), "MASA1": LTV_DIR.joinpath("ltv48_MASA1TC.wav"), "MASA2": LTV_DIR.joinpath("ltv48_MASA2TC.wav"), - "OMASA_1_1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.wav"), - "OMASA_1_2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.wav"), - "OMASA_1_3": LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.wav"), - "OMASA_1_4": LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.wav"), - "OMASA_2_1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.wav"), - "OMASA_2_2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.wav"), - "OMASA_2_3": LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.wav"), - "OMASA_2_4": LTV_DIR.joinpath("ltv48_OMASA_4ISM_2TC.wav"), - "OSBA_1_1": LTV_DIR.joinpath("ltv48_OSBA_1ISM_FOA.wav"), - "OSBA_1_2": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA2.wav"), - "OSBA_1_3": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA3.wav"), - "OSBA_2_1": LTV_DIR.joinpath("ltv48_OSBA_2ISM_FOA.wav"), - "OSBA_2_2": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA2.wav"), - "OSBA_2_3": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA3.wav"), - "OSBA_3_1": LTV_DIR.joinpath("ltv48_OSBA_3ISM_FOA.wav"), - "OSBA_3_2": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA2.wav"), - "OSBA_3_3": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA3.wav"), - "OSBA_4_1": LTV_DIR.joinpath("ltv48_OSBA_4ISM_FOA.wav"), - "OSBA_4_2": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA2.wav"), - "OSBA_4_3": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA3.wav"), - "META": TEST_VECTOR_DIR.joinpath("mixed_scene.txt"), + "ISM1MASA1": LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.wav"), + "ISM2MASA1": LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.wav"), + "ISM3MASA1": LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.wav"), + "ISM4MASA1": LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.wav"), + "ISM1MASA2": LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.wav"), + "ISM2MASA2": LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.wav"), + "ISM3MASA2": LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.wav"), + "ISM4MASA2": LTV_DIR.joinpath("ltv48_OMASA_4ISM_2TC.wav"), + "ISM1SBA1": LTV_DIR.joinpath("ltv48_OSBA_1ISM_FOA.wav"), + "ISM1SBA2": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA2.wav"), + "ISM1SBA3": LTV_DIR.joinpath("ltv48_OSBA_1ISM_HOA3.wav"), + "ISM2SBA1": LTV_DIR.joinpath("ltv48_OSBA_2ISM_FOA.wav"), + "ISM2SBA2": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA2.wav"), + "ISM2SBA3": LTV_DIR.joinpath("ltv48_OSBA_2ISM_HOA3.wav"), + "ISM3SBA1": LTV_DIR.joinpath("ltv48_OSBA_3ISM_FOA.wav"), + "ISM3SBA2": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA2.wav"), + "ISM3SBA3": LTV_DIR.joinpath("ltv48_OSBA_3ISM_HOA3.wav"), + "ISM4SBA1": LTV_DIR.joinpath("ltv48_OSBA_4ISM_FOA.wav"), + "ISM4SBA2": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA2.wav"), + "ISM4SBA3": LTV_DIR.joinpath("ltv48_OSBA_4ISM_HOA3.wav"), + "META": TEST_VECTOR_DIR.joinpath("mixed_scene_48.txt"), "16ch_8+4+4": LTV_DIR.joinpath("ltv48_HOA3.wav"), - "4d4": LTV_DIR.joinpath("ltv48_MC71.wav"), "t_design_4": LTV_DIR.joinpath("ltv48_MC714.wav"), } @@ -252,44 +251,44 @@ FORMAT_TO_METADATA_FILES = { ], "MASA1": [str(TESTV_DIR.joinpath("stv1MASA1TC48c.met"))], "MASA2": [str(TESTV_DIR.joinpath("stv2MASA2TC48c.met"))], - "OMASA_1_1": [ + "ISM1MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA1TC48c.met")), ], - "OMASA_1_2": [ + "ISM2MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA1TC48c.met")), ], - "OMASA_1_3": [ + "ISM3MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA1TC48c.met")), ], - "OMASA_1_4": [ + "ISM4MASA1": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvISM4.csv")), str(TESTV_DIR.joinpath("stvOMASA_4ISM_2MASA1TC48c.met")), ], - "OMASA_2_1": [ + "ISM1MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvOMASA_1ISM_1MASA2TC48c.met")), ], - "OMASA_2_2": [ + "ISM2MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvOMASA_2ISM_2MASA2TC48c.met")), ], - "OMASA_2_3": [ + "ISM3MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), str(TESTV_DIR.joinpath("stvOMASA_3ISM_1MASA2TC48c.met")), ], - "OMASA_2_4": [ + "ISM4MASA2": [ str(TESTV_DIR.joinpath("stvISM1.csv")), str(TESTV_DIR.joinpath("stvISM2.csv")), str(TESTV_DIR.joinpath("stvISM3.csv")), @@ -315,7 +314,7 @@ FORMAT_TO_METADATA_FILES_LTV = { str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltvISM4.csv")), ], - "NDP_ISM4": [ # Should not be needed, because it is included in all ISM metadata files. + "NDP_ISM4": [ # Should not be needed, because it is included in all ISM metadata files. str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), @@ -323,44 +322,44 @@ FORMAT_TO_METADATA_FILES_LTV = { ], "MASA1": [str(LTV_DIR.joinpath("ltv48_MASA1TC.met"))], "MASA2": [str(LTV_DIR.joinpath("ltv48_MASA2TC.met"))], - "OMASA_1_1": [ + "ISM1MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_1ISM_1TC.met")), ], - "OMASA_1_2": [ + "ISM2MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.met")), ], - "OMASA_1_3": [ + "ISM3MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_3ISM_1TC.met")), ], - "OMASA_1_4": [ + "ISM4MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltvISM4.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_4ISM_1TC.met")), ], - "OMASA_2_1": [ + "ISM1MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_1ISM_2TC.met")), ], - "OMASA_2_2": [ + "ISM2MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_2TC.met")), ], - "OMASA_2_3": [ + "ISM3MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), str(LTV_DIR.joinpath("ltv48_OMASA_3ISM_2TC.met")), ], - "OMASA_2_4": [ + "ISM4MASA2": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), str(LTV_DIR.joinpath("ltvISM3.csv")), @@ -374,6 +373,31 @@ INPUT_FORMATS_AMBI = ["FOA", "HOA2", "HOA3"] INPUT_FORMATS_MC = ["MONO", "STEREO", "5_1", "5_1_2", "5_1_4", "7_1", "7_1_4"] INPUT_FORMATS_ISM = ["ISM1", "ISM2", "ISM3", "ISM4"] INPUT_FORMATS_MASA = ["MASA1", "MASA2"] +INPUT_FORMATS_OMASA = [ + "ISM1MASA1", + "ISM2MASA1", + "ISM3MASA1", + "ISM4MASA1", + "ISM1MASA2", + "ISM2MASA2", + "ISM3MASA2", + "ISM4MASA2", +] +INPUT_FORMATS_OSBA = [ + "ISM1SBA1", + "ISM1SBA2", + "ISM1SBA3", + "ISM2SBA1", + "ISM2SBA2", + "ISM2SBA3", + "ISM3SBA1", + "ISM3SBA2", + "ISM3SBA3", + "ISM4SBA1", + "ISM4SBA2", + "ISM4SBA3", +] + """ Non binaural / parametric output formats """ OUTPUT_FORMATS = [ @@ -392,14 +416,13 @@ OUTPUT_FORMATS = [ """ Custom loudspeaker input/output """ CUSTOM_LS_TO_TEST = [ "t_design_4", - "4d4", "16ch_8+4+4", ] """ Mixed scene ( metadata ) rendering """ METADATA_SCENES_TO_TEST = ["mixed_scene", "mixed_scene_simple"] METADATA_SCENES_TO_TEST_NO_BE = ["masa_scene"] -METADATA_SCENES_TO_TEST_MASA_PREREND = ["mixed_mc714_foa_masa2_ism4"] +METADATA_SCENES_TO_TEST_MASA_PREREND = ["mixed_10sec_mc714_foa_masa1_ism4", "mixed_10sec_mc714_foa_masa2_ism4"] """ Binaural rendering """ OUTPUT_FORMATS_BINAURAL = ["BINAURAL", "BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"] @@ -418,4 +441,3 @@ PEAQ_SUPPORTED_FMT = [ "BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB", ] - diff --git a/tests/renderer_short/test_renderer.py b/tests/renderer_short/test_renderer.py index 8cfda2f6db..c49750928e 100644 --- a/tests/renderer_short/test_renderer.py +++ b/tests/renderer_short/test_renderer.py @@ -33,25 +33,26 @@ the United Nations Convention on Contracts on the International Sales of Goods. import pytest from .constants import ( - FORMAT_TO_METADATA_FILES_LTV, - OUTPUT_FORMATS, - INPUT_FORMATS_AMBI, - FRAMING_TO_TEST, + CUSTOM_LAYOUT_DIR, + CUSTOM_LS_TO_TEST, EXE_SUFFIX, - OUTPUT_FORMATS_BINAURAL, + FRAMING_TO_TEST, HR_TRAJECTORIES_TO_TEST, HR_TRAJECTORY_DIR, - INPUT_FORMATS_MC, + INPUT_FORMATS_AMBI, INPUT_FORMATS_ISM, INPUT_FORMATS_MASA, + INPUT_FORMATS_MC, + INPUT_FORMATS_OMASA, + INPUT_FORMATS_OSBA, + METADATA_SCENES_TO_TEST, METADATA_SCENES_TO_TEST_MASA_PREREND, + OUTPUT_FORMATS, + OUTPUT_FORMATS_BINAURAL, + SAMPLING_RATES, TEST_VECTOR_DIR, - CUSTOM_LS_TO_TEST, - CUSTOM_LAYOUT_DIR, - METADATA_SCENES_TO_TEST, ) -from .utils import run_renderer, compare_renderer_args -from ..conftest import props_to_record +from .utils import compare_renderer_args, run_renderer ############################################################################## # Bit-exactness tests @@ -65,6 +66,7 @@ from ..conftest import props_to_record @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics( record_property, props_to_record, @@ -72,6 +74,7 @@ def test_ambisonics( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -79,6 +82,26 @@ def test_ambisonics( get_odg_bin, split_comparison, ): + if in_fmt == "HOA2": + if out_fmt != "5_1": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + if fs != "48kHz": + pytest.skip() + + if in_fmt == "FOA": + if frame_size != "5ms": + pytest.skip() + if fs != "32kHz": + pytest.skip() + + if in_fmt == "HOA3": + if frame_size != "20ms": + pytest.skip() + if fs == "32kHz": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -93,12 +116,14 @@ def test_ambisonics( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics_binaural_static( record_property, props_to_record, @@ -106,6 +131,7 @@ def test_ambisonics_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -113,6 +139,15 @@ def test_ambisonics_binaural_static( get_odg_bin, split_comparison, ): + if in_fmt != "HOA2": + pytest.skip() + + if frame_size != "20ms": + pytest.skip() + + if fs != "48kHz": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -127,6 +162,7 @@ def test_ambisonics_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -134,6 +170,7 @@ def test_ambisonics_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ambisonics_binaural_headrotation( record_property, props_to_record, @@ -142,6 +179,7 @@ def test_ambisonics_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -149,6 +187,14 @@ def test_ambisonics_binaural_headrotation( get_odg_bin, split_comparison, ): + if in_fmt == "HOA2": + pytest.skip() + + if in_fmt == "FOA" and frame_size != "20ms" and fs != "48kHz": + pytest.skip() + + # Run everything for HOA3 input + run_renderer( record_property, props_to_record, @@ -164,14 +210,25 @@ def test_ambisonics_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.skip(reason="Not supported for BASOP code currently") @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[2:]) -@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize( + "in_fmt", + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], +) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) @pytest.mark.parametrize("aeid", ["1", "0"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_dynamic_acoustic_environment( record_property, props_to_record, @@ -179,6 +236,7 @@ def test_dynamic_acoustic_environment( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -187,8 +245,20 @@ def test_dynamic_acoustic_environment( aeid, split_comparison, ): - rend_config_path = TEST_VECTOR_DIR.joinpath(f"rend_config_combined.cfg") - rend_config_path.with_stem(f"rend_config") + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + if in_fmt not in ["ISM4", "HOA3", "5_1", "MASA2", "ISM1MASA1", "ISM1SBA1"]: + pytest.skip() + + if frame_size == "5ms": + pytest.skip() + + if fs == "32kHz" and in_fmt in ["ISM4", "HOA3", "ISM1MASA1"]: + pytest.skip() + + rend_config_path = TEST_VECTOR_DIR.joinpath("rend_config_combined.cfg") + rend_config_path.with_stem("rend_config") run_renderer( record_property, @@ -206,13 +276,24 @@ def test_dynamic_acoustic_environment( config_file=rend_config_path, aeid=aeid, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.skip("MSAN errors in BASOP need to be fixed") @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL[2:]) -@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI) +@pytest.mark.parametrize( + "in_fmt", + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], +) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_dynamic_acoustic_environment_file( record_property, props_to_record, @@ -220,6 +301,7 @@ def test_dynamic_acoustic_environment_file( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -227,10 +309,22 @@ def test_dynamic_acoustic_environment_file( get_odg_bin, split_comparison, ): - rend_config_path = TEST_VECTOR_DIR.joinpath(f"rend_config_combined.cfg") - rend_config_path.with_stem(f"rend_config") + if in_fmt in ["MONO", "STEREO"]: + pytest.skip("MONO or STEREO to Binaural rendering unsupported") + + if in_fmt not in ["ISM4", "HOA3", "5_1", "MASA2", "ISM1MASA1", "ISM1SBA1"]: + pytest.skip() + + if frame_size == "5ms": + pytest.skip() + + if fs == "16kHz" and in_fmt not in ["ISM4", "HOA3", "ISM1MASA1"]: + pytest.skip() - aeid = TEST_VECTOR_DIR.joinpath(f"aeid1.txt") + rend_config_path = TEST_VECTOR_DIR.joinpath("rend_config_combined.cfg") + rend_config_path.with_stem("rend_config") + + aeid = TEST_VECTOR_DIR.joinpath("aeid1.txt") run_renderer( record_property, @@ -248,6 +342,7 @@ def test_dynamic_acoustic_environment_file( config_file=rend_config_path, aeid=aeid, split_comparison=split_comparison, + sr=fs, ) @@ -257,6 +352,7 @@ def test_dynamic_acoustic_environment_file( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel( record_property, props_to_record, @@ -264,6 +360,7 @@ def test_multichannel( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -271,6 +368,18 @@ def test_multichannel( get_odg_bin, split_comparison, ): + if in_fmt not in ["STEREO", "7_1_4"]: + if frame_size != "20ms": + pytest.skip() + if fs != "48kHz": + pytest.skip() + if out_fmt not in ["MONO", "STEREO", "FOA", "7_1_4"]: + pytest.skip() + if frame_size != "5ms": + pytest.skip() + + # Run everything for 7_1_4 input + run_renderer( record_property, props_to_record, @@ -285,12 +394,14 @@ def test_multichannel( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel_binaural_static( record_property, props_to_record, @@ -298,6 +409,7 @@ def test_multichannel_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -308,6 +420,15 @@ def test_multichannel_binaural_static( if in_fmt in ["MONO", "STEREO"]: pytest.skip("MONO or STEREO to Binaural rendering unsupported") + if fs != "48kHz": + pytest.skip() + + if frame_size != "20ms": + pytest.skip() + + if in_fmt not in ["5_1", "7_1_4"]: + pytest.skip() + run_renderer( record_property, props_to_record, @@ -322,6 +443,7 @@ def test_multichannel_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -329,6 +451,7 @@ def test_multichannel_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_multichannel_binaural_headrotation( record_property, props_to_record, @@ -337,6 +460,7 @@ def test_multichannel_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -347,6 +471,16 @@ def test_multichannel_binaural_headrotation( if in_fmt in ["MONO", "STEREO"]: pytest.skip("MONO or STEREO to Binaural rendering unsupported") + if in_fmt != "5_1_4": + if fs != "48kHz": + pytest.skip() + if in_fmt in ["MONO, 5_1, 7_1_4"] and frame_size != "20ms": + pytest.skip() + if in_fmt not in ["MONO, 5_1, 7_1_4"] and frame_size == "20ms": + pytest.skip() + + # Run everything for 5_1_4 + run_renderer( record_property, props_to_record, @@ -362,6 +496,7 @@ def test_multichannel_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -371,6 +506,7 @@ def test_multichannel_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism( record_property, props_to_record, @@ -378,6 +514,7 @@ def test_ism( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -385,6 +522,22 @@ def test_ism( get_odg_bin, split_comparison, ): + if in_fmt != "ISM4": + if frame_size != "20ms": + pytest.skip() + if fs != "48kHz": + pytest.skip() + if out_fmt not in ["STEREO", "5_1"]: + pytest.skip() + + if out_fmt not in ["HOA3", "5_1_2"]: + if frame_size != "5ms": + pytest.skip() + if fs != "32kHz": + pytest.skip() + + # Run almost everything for ISM4 + run_renderer( record_property, props_to_record, @@ -399,12 +552,14 @@ def test_ism( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism_binaural_static( record_property, props_to_record, @@ -412,6 +567,7 @@ def test_ism_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -419,6 +575,20 @@ def test_ism_binaural_static( get_odg_bin, split_comparison, ): + if in_fmt != "ISM4": + if frame_size != "20ms": + pytest.skip() + if fs != "48kHz": + pytest.skip() + + if fs == "32kHz" and frame_size == "5ms": + pytest.skip() + + if fs == "16kHz" and frame_size == "20ms": + pytest.skip() + + # Run everything for ISM4 + run_renderer( record_property, props_to_record, @@ -433,6 +603,7 @@ def test_ism_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -440,6 +611,7 @@ def test_ism_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_ism_binaural_headrotation( record_property, props_to_record, @@ -448,6 +620,7 @@ def test_ism_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -455,6 +628,14 @@ def test_ism_binaural_headrotation( get_odg_bin, split_comparison, ): + if in_fmt != "ISM4": + if frame_size != "5ms": + pytest.skip() + if fs != "32kHz": + pytest.skip() + + # Run everything for ISM4 + run_renderer( record_property, props_to_record, @@ -470,6 +651,7 @@ def test_ism_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -479,6 +661,7 @@ def test_ism_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa( record_property, props_to_record, @@ -486,6 +669,7 @@ def test_masa( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -493,6 +677,46 @@ def test_masa( get_odg_bin, split_comparison, ): + if fs == "32kHz" and out_fmt in [ + "STEREO", + "5_1", + "5_1_2", + "5_1_4", + "7_1", + "7_1_4", + "FOA", + "HOA2", + ]: + pytest.skip() + + if fs == "16kHz" and out_fmt in [ + "5_1_2", + "5_1_4", + "7_1", + "7_1_4", + "FOA", + "HOA2", + "HOA3", + ]: + pytest.skip() + + if fs in ["16kHz", "32kHz"] and frame_size == "5ms": + pytest.skip() + + if ( + out_fmt in ["5_1_2", "5_1_4", "7_1", "HOA2", "HOA3"] + and frame_size != "20ms" + and in_fmt == "MASA1" + ): + pytest.skip() + + if ( + out_fmt in ["5_1_2", "7_1_4", "5_1", "FOA"] + and frame_size != "20ms" + and in_fmt == "MASA2" + ): + pytest.skip() + run_renderer( record_property, props_to_record, @@ -507,12 +731,14 @@ def test_masa( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_binaural_static( record_property, props_to_record, @@ -520,6 +746,7 @@ def test_masa_binaural_static( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -527,8 +754,10 @@ def test_masa_binaural_static( get_odg_bin, split_comparison, ): - if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: - pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") + if fs in ["16kHz", "32kHz"] and frame_size == "5ms": + pytest.skip() + + # fs == 48 tests everything run_renderer( record_property, @@ -544,6 +773,7 @@ def test_masa_binaural_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -551,6 +781,7 @@ def test_masa_binaural_static( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_binaural_headrotation( record_property, props_to_record, @@ -559,6 +790,7 @@ def test_masa_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -566,8 +798,13 @@ def test_masa_binaural_headrotation( get_odg_bin, split_comparison, ): - if out_fmt in ["BINAURAL_ROOM_IR", "BINAURAL_ROOM_REVERB"]: - pytest.skip("Skipping binaural room outputs for MASA as unimplemented.") + if fs == 32 and frame_size == "20ms": + pytest.skip() + + if fs == 16 and frame_size == "5ms": + pytest.skip() + + # fs == 48 tests everything run_renderer( record_property, @@ -584,15 +821,57 @@ def test_masa_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) -@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST_MASA_PREREND) +@pytest.mark.parametrize("out_fmt", INPUT_FORMATS_MASA) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_masa_prerend( record_property, props_to_record, test_info, in_fmt, + out_fmt, + fs, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt not in ["ISM4MASA2", "ISM1MASA1"] and fs != "48kHz": + pytest.skip() + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", INPUT_FORMATS_MASA) +@pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST_MASA_PREREND) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_masa_prerend_scenes( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -600,20 +879,326 @@ def test_masa_prerend( get_odg_bin, split_comparison, ): + # Test all scenes + run_renderer( record_property, props_to_record, test_info, "META", - "MASA2", - metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + out_fmt, + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}_{fs[:2]}.txt"), + binary_suffix=EXE_SUFFIX, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +""" OMASA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt != "ISM4MASA2": + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + if out_fmt not in ["STEREO", "5_1"]: + pytest.skip() + + if in_fmt in ["ISM3MASA1", "ISM2MASA2", "ISM1MASA1"]: + pytest.skip() + + if out_fmt in ["5_1", "5_1_4", "7_1_4", "FOA", "HOA2"]: + if frame_size != "20ms": + pytest.skip() + if fs != "16kHz": + pytest.skip() + + # Test all for ISM4MASA2 + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa_binaural_static( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt != "ISM4MASA2": + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + + if in_fmt in ["ISM3MASA2", "ISM2MASA1", "ISM1MASA2", "ISM4MASA1"]: + pytest.skip() + + # Test all for ISM4MASA2 + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_omasa_binaural_headrotation( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + trj_file, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt not in ["ISM1MASA1", "ISM2MASA2", "ISM4MASA2"]: + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + + # Test all for ["ISM1MASA1", "ISM2MASA2", "ISM4MASA2"] + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), binary_suffix=EXE_SUFFIX, + frame_size=frame_size, get_mld=get_mld, mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, + ) + + +""" OSBA """ + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt != "ISM4SBA3": + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + if out_fmt not in ["HOA3", "5_1"]: + pytest.skip() + + if out_fmt in ["HOA3", "5_1"]: + pytest.skip() + # Test all for ISM4SBA3 + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba_binaural_static( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt != "ISM4SBA3": + pytest.skip() + + if fs != "48kHz": + pytest.skip() + + if frame_size != "20ms": + pytest.skip() + + # Test all for ISM4SBA3 + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, + ) + + +@pytest.mark.parametrize("trj_file", HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS_BINAURAL) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA) +@pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) +def test_osba_binaural_headrotation( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + fs, + trj_file, + frame_size, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + split_comparison, +): + if in_fmt not in ["ISM1SBA1", "ISM2SBA2", "ISM4SBA3"]: + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + + # Test all for ["ISM1SBA1", "ISM2SBA2", "ISM4SBA3"] + + run_renderer( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + trj_file=HR_TRAJECTORY_DIR.joinpath(f"{trj_file}.csv"), + binary_suffix=EXE_SUFFIX, + frame_size=frame_size, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + split_comparison=split_comparison, + sr=fs, ) @@ -623,6 +1208,7 @@ def test_masa_prerend( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_layout", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input( record_property, props_to_record, @@ -630,6 +1216,7 @@ def test_custom_ls_input( in_layout, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -637,6 +1224,14 @@ def test_custom_ls_input( get_odg_bin, split_comparison, ): + if out_fmt not in ["7_1, FOA, STEREO"]: + if fs != "48kHz": + pytest.skip() + if frame_size != "20ms": + pytest.skip() + + # Test rest + run_renderer( record_property, props_to_record, @@ -651,20 +1246,30 @@ def test_custom_ls_input( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize( "in_fmt", - [*INPUT_FORMATS_AMBI, *INPUT_FORMATS_MC, *INPUT_FORMATS_ISM, *INPUT_FORMATS_MASA], + [ + *INPUT_FORMATS_AMBI, + *INPUT_FORMATS_MC, + *INPUT_FORMATS_ISM, + *INPUT_FORMATS_MASA, + *INPUT_FORMATS_OMASA, + *INPUT_FORMATS_OSBA, + ], ) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_output( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -672,11 +1277,23 @@ def test_custom_ls_output( get_odg_bin, split_comparison, ): - # TODO: revert once BASOP is brought up-to-date - if in_fmt in INPUT_FORMATS_MASA: - pytest.skip("MASA to custom LS not supported on ivas-float-update yet") - if in_fmt in INPUT_FORMATS_ISM and out_fmt == "t_design_4": - pytest.skip("ISMx + t_design_4 skipped until bug in BASOP is fixed") + if in_fmt not in [ + "HOA3", + "MONO", + "STEREO", + "5_1", + "7_1_4", + "MASA1", + "MASA2", + "ISM4", + "ISM4MASA2", + "ISM4SBA3", + ]: + pytest.skip() + + if out_fmt == "t_design_4" and fs != "48kHz": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -690,17 +1307,20 @@ def test_custom_ls_output( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", CUSTOM_LS_TO_TEST) @pytest.mark.parametrize("in_fmt", CUSTOM_LS_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_output( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, get_mld, get_mld_lim, get_ssnr, @@ -721,12 +1341,14 @@ def test_custom_ls_input_output( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @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) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_binaural( record_property, props_to_record, @@ -734,6 +1356,7 @@ def test_custom_ls_input_binaural( in_layout, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -741,6 +1364,9 @@ def test_custom_ls_input_binaural( get_odg_bin, split_comparison, ): + if fs != "48kHz" or frame_size != "20ms": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -755,6 +1381,7 @@ def test_custom_ls_input_binaural( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -762,6 +1389,7 @@ def test_custom_ls_input_binaural( @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) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_custom_ls_input_binaural_headrotation( record_property, props_to_record, @@ -770,6 +1398,7 @@ def test_custom_ls_input_binaural_headrotation( out_fmt, trj_file, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -777,6 +1406,12 @@ def test_custom_ls_input_binaural_headrotation( get_odg_bin, split_comparison, ): + if fs == "32kHz" and frame_size == "5ms": + pytest.skip() + + if fs == "16kHz" and frame_size == "20ms": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -792,6 +1427,7 @@ def test_custom_ls_input_binaural_headrotation( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -801,6 +1437,7 @@ def test_custom_ls_input_binaural_headrotation( @pytest.mark.parametrize("out_fmt", OUTPUT_FORMATS) @pytest.mark.parametrize("in_fmt", METADATA_SCENES_TO_TEST) @pytest.mark.parametrize("frame_size", FRAMING_TO_TEST) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_metadata( record_property, props_to_record, @@ -808,6 +1445,7 @@ def test_metadata( in_fmt, out_fmt, frame_size, + fs, get_mld, get_mld_lim, get_ssnr, @@ -815,13 +1453,22 @@ def test_metadata( get_odg_bin, split_comparison, ): + if fs == "32kHz" and frame_size == "5ms": + pytest.skip() + + if fs == "16kHz" and frame_size == "20ms": + pytest.skip() + + if out_fmt not in ["5_1_2", "HOA3", "STEREO"] and fs != "48kHz": + pytest.skip() + run_renderer( record_property, props_to_record, test_info, "META", out_fmt, - metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}.txt"), + metadata_input=TEST_VECTOR_DIR.joinpath(f"{in_fmt}_{fs[:2]}.txt"), binary_suffix=EXE_SUFFIX, frame_size=frame_size, get_mld=get_mld, @@ -830,6 +1477,7 @@ def test_metadata( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -839,6 +1487,7 @@ def test_metadata( @pytest.mark.parametrize("out_fmt", ["STEREO"]) @pytest.mark.parametrize("in_fmt", ["MONO"]) @pytest.mark.parametrize("non_diegetic_pan", ["0", "-30", "45", "90", "-90"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_non_diegetic_pan_static( record_property, props_to_record, @@ -846,6 +1495,7 @@ def test_non_diegetic_pan_static( in_fmt, out_fmt, non_diegetic_pan, + fs, get_mld, get_mld_lim, get_ssnr, @@ -853,6 +1503,12 @@ def test_non_diegetic_pan_static( get_odg_bin, split_comparison, ): + if non_diegetic_pan in ["0", "45", "-90"] and fs == "32kHz": + pytest.skip() + + if non_diegetic_pan not in ["0", "45", "-90"] and fs == "16kHz": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -867,18 +1523,21 @@ def test_non_diegetic_pan_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @pytest.mark.parametrize("out_fmt", ["STEREO"]) @pytest.mark.parametrize("in_fmt", ["ISM1"]) @pytest.mark.parametrize("non_diegetic_pan", ["0", "-30", "45", "90", "-90"]) +@pytest.mark.parametrize("fs", SAMPLING_RATES) def test_non_diegetic_pan_ism_static( record_property, props_to_record, test_info, in_fmt, out_fmt, + fs, non_diegetic_pan, get_mld, get_mld_lim, @@ -887,6 +1546,12 @@ def test_non_diegetic_pan_ism_static( get_odg_bin, split_comparison, ): + if non_diegetic_pan in ["0", "45", "-90"] and fs == "16kHz": + pytest.skip() + + if non_diegetic_pan not in ["0", "45", "-90"] and fs == "32kHz": + pytest.skip() + run_renderer( record_property, props_to_record, @@ -901,6 +1566,7 @@ def test_non_diegetic_pan_ism_static( get_odg=get_odg, get_odg_bin=get_odg_bin, split_comparison=split_comparison, + sr=fs, ) @@ -1044,31 +1710,27 @@ def test_ambisonics_binaural_headrotation_refvecequal( if test_info.config.option.create_ref or test_info.config.option.create_cut: pytest.skip("OTR tests only run for smoke test") - # TODO revert - if in_fmt == "HOA3" and out_fmt == "BINAURAL_ROOM_REVERB": - pytest.xfail("WIP : minor differences to be resolved") - else: - compare_renderer_args( - record_property, - props_to_record, - test_info, - in_fmt, - out_fmt, - ref_kwargs={ - "name_extension": "refvecequal", - "frame_size": "5", - }, - cut_kwargs={ - "trj_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s.csv" - ), - "refvec_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s-Vector3.csv" - ), - "frame_size": "5", - }, - split_comparison=split_comparison, - ) + compare_renderer_args( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + ref_kwargs={ + "name_extension": "refvecequal", + "frame_size": "5", + }, + cut_kwargs={ + "trj_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s.csv" + ), + "refvec_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s-Vector3.csv" + ), + "frame_size": "5", + }, + split_comparison=split_comparison, + ) # This test compares rendering with: @@ -1088,32 +1750,28 @@ def test_ambisonics_binaural_headrotation_refvec_rotating( if test_info.config.option.create_ref or test_info.config.option.create_cut: pytest.skip("OTR tests only run for smoke test") - # TODO revert - if in_fmt == "HOA2" and out_fmt == "BINAURAL_ROOM_REVERB": - pytest.xfail("WIP : minor differences to be resolved") - else: - compare_renderer_args( - record_property, - props_to_record, - test_info, - in_fmt, - out_fmt, - ref_kwargs={ - "name_extension": "refvec_rotating", - "trj_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s.csv" - ), - "frame_size": "5", - }, - cut_kwargs={ - "trj_file": HR_TRAJECTORY_DIR.joinpath("const000.csv"), - "refvec_file": HR_TRAJECTORY_DIR.joinpath( - "full-circle-with-up-and-down-4s-ccw-Vector3.csv" - ), - "frame_size": "5", - }, - split_comparison=split_comparison, - ) + compare_renderer_args( + record_property, + props_to_record, + test_info, + in_fmt, + out_fmt, + ref_kwargs={ + "name_extension": "refvec_rotating", + "trj_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s.csv" + ), + "frame_size": "5", + }, + cut_kwargs={ + "trj_file": HR_TRAJECTORY_DIR.joinpath("const000.csv"), + "refvec_file": HR_TRAJECTORY_DIR.joinpath( + "full-circle-with-up-and-down-4s-ccw-Vector3.csv" + ), + "frame_size": "5", + }, + split_comparison=split_comparison, + ) # This test compares rendering with: diff --git a/tests/renderer_short/utils.py b/tests/renderer_short/utils.py index 9afda68b94..1aa7370f08 100644 --- a/tests/renderer_short/utils.py +++ b/tests/renderer_short/utils.py @@ -30,44 +30,44 @@ accordance with the laws of the Federal Republic of Germany excluding its confli the United Nations Convention on Contracts on the International Sales of Goods. """ +import errno import filecmp import logging import os -from pathlib import Path +import re import subprocess as sp import sys +import tempfile +from pathlib import Path from typing import Dict, Optional, Union import numpy as np import pytest -import re -import errno -import tempfile +from ..constants import CAT_NORMAL from .compare_audio import compare_audio_arrays from .constants import ( - LTV_DIR, - SCRIPTS_DIR, - OUTPUT_PATH_REF, - OUTPUT_PATH_CUT, + BIN_SUFFIX_MERGETARGET, FORMAT_TO_FILE_COMPARETEST, FORMAT_TO_FILE_LTV, + FORMAT_TO_FILE_SMOKETEST, FORMAT_TO_METADATA_FILES, FORMAT_TO_METADATA_FILES_LTV, - FORMAT_TO_FILE_SMOKETEST, - RENDERER_CMD, - BIN_SUFFIX_MERGETARGET, + LTV_DIR, + OUTPUT_PATH_CUT, + OUTPUT_PATH_REF, PEAQ_SUPPORTED_FMT, + RENDERER_CMD, + SCRIPTS_DIR, ) -from ..constants import CAT_NORMAL sys.path.append(SCRIPTS_DIR) from pyaudio3dtools.audiofile import readfile -from ..cmp_pcm import cmp_pcm -from ..conftest import parse_properties, get_split_idx +from ..cmp_pcm import cmp_pcm +from ..conftest import get_split_idx, parse_properties -def _run_cmd(cmd, env, test_info=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 @@ -92,29 +92,30 @@ def _run_cmd(cmd, env, test_info=None): raise SystemError(error) -def run_cmd(cmd, test_info, env=None): +def run_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env, test_info) + _run_cmd(cmd, test_info=test_info, env=env) -def run_isar_ext_rend_cmd(cmd, env=None): +def run_isar_ext_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR EXT REND command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env) + _run_cmd(cmd, test_info=test_info, env=env) -def run_ivas_isar_enc_cmd(cmd, env=None): +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, env) - - -def run_ivas_isar_dec_cmd(cmd, env=None): - logging.info(f"\nDUT decoder command:\n\t{' '.join(cmd)}\n") - _run_cmd(cmd, env) + _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") + else: + 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, env=None): +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, env) + _run_cmd(cmd, test_info=test_info, env=env) def check_BE( @@ -175,7 +176,7 @@ def run_renderer( aeid: Optional[Union[Path, int]] = None, in_file=None, out_file=None, - sr=48, + sr="48kHz", render_for_peaq=False, split_comparison=False, ) -> str: @@ -267,12 +268,20 @@ def run_renderer( else: in_file = format_to_file[in_fmt] in_name = in_fmt + in_file = str(in_file).replace("48", sr[:2]) if in_meta_files is None and in_fmt in format_to_metadata_files: in_meta_files = format_to_metadata_files[in_fmt] + # If metadata not given with ISM input, use default NULL + if in_meta_files is None and isinstance(in_fmt, str) and "ism" in in_fmt.lower(): + match = re.search(r"ism(\d)", in_fmt.lower()) + assert match is not None + num_obj = int(match[1]) + in_meta_files = ["NULL"] * num_obj + if out_file is None: - 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}{aeid_name}.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}{aeid_name}_{sr}.wav" out_file = str(output_path_base.joinpath(out_file_stem)) cmd = RENDERER_CMD[:] @@ -280,7 +289,7 @@ def run_renderer( cmd[4] = str(in_fmt) cmd[6] = str(out_file) cmd[8] = str(out_fmt) - cmd[10] = str(sr) + cmd[10] = str(sr[:2]) if test_info.config.option.create_ref: cmd[0] += BIN_SUFFIX_MERGETARGET @@ -370,7 +379,7 @@ def run_renderer( cmd2[4] = str(out_fmt) # in_fmt cmd2[6] = odg_test # out_file cmd2[8] = new_fmt # out_fmt - cmd2[10] = str(sr) + cmd2[10] = str(sr[:2]) cmd2[0] += BIN_SUFFIX_MERGETARGET # Use IVAS_rend_ref for re-rendering cmd2[0] += binary_suffix if "MASA" in str(out_fmt): @@ -551,8 +560,8 @@ def binauralize_input_and_output( # If extended metadata is not used, strip the metadata for the external renderer extended_md_used = ( re.search(r"-ism\s?\+[1-4]", enc_opts) - and not "OMASA" in in_fmt - and not "OSBA" in in_fmt + and "OMASA" not in in_fmt + and "OSBA" not in in_fmt ) if not extended_md_used and n_obj > 0: truncated_meta_files = [] @@ -634,7 +643,7 @@ def binauralize_input_and_output( aeid = findstr(r"-aeid\s+(\S+)", dec_opts) - if not output_config.upper() in PEAQ_SUPPORTED_FMT: + if output_config.upper() not in PEAQ_SUPPORTED_FMT: # Render output to BINAURAL output_reformat = "BINAURAL" diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index b09333fc46..5b1bb2be79 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -389,11 +389,69 @@ class ACOUSTIC_ENVIRONMENT: abscoeff: tuple[float, float, float, float, float, float] = () +@dataclass +class ISM_NUM: + num: int = 1 + +@dataclass +class ISM_ID: + ids: list[int] + +@dataclass +class ISM_GAIN: + gains: list[int] + +@dataclass +class ISM_ORIENTATION: + orientations: list[ORIENTATION] + +@dataclass +class ISM_POSITION: + positions: list[POSITION] + +@dataclass +class DISTANCE_ATTENUATION: + ref_dist: float = 1.0 + max_dist: int = 10 + roll_off: float = 1.0 + +@dataclass +class ISM_DISTANCE_ATTENUATION: + distance_attenuations: list[DISTANCE_ATTENUATION] + +@dataclass +class DIRECTIVITY: + inner_ang: int = 360 + outer_ang: int = 0 + outer_att: int = 0 + +@dataclass +class ISM_DIRECTIVITY: + directivities: list[DIRECTIVITY] + +@dataclass +class R_ISM_ID: + id: int + +@dataclass +class R_ISM_GAIN: + gain: int + +@dataclass +class R_ISM_DIRECTION: + azi: float + elev: float + @dataclass class AUDIO_FOCUS: direction: Optional[ORIENTATION] = None level: Optional[AUDIO_FOCUS_LEVEL] = None +@dataclass +class PI_LATENCY: + reverseType: PIDATAS + latency: int + @dataclass class PIDATA: @@ -402,7 +460,7 @@ class PIDATA: data: any = None -MAX_PACKED_PI_SIZE = 32 +MAX_PACKED_PI_SIZE = 48 ivasBitrates = [ 13200, 16400, @@ -581,10 +639,21 @@ roomDimensionValue = [ 90.51, ] absorptionCoeffValues = [0.0800, 0.1656, 0.3430, 0.7101] +ismGains = list(range(0, -25, -1)) + [-128] + list(range(1, 13)) # -128 corresponds to -Inf +refDistances = [round(x * 0.1, 1) for x in range(1, 65)] +maxDistances = list(range(1,65)) +rolloffFactors = [round(x * 0.1, 1) for x in range(0, 41)] +innerOuterAngles = list(range(0,361,15)) +outerAttenuations = [-128] + list(range(-90,1,3)) # -128 corresponds to -Inf codedFormats = list(FORMATS) codedSubFormats = list(SUBFORMATS) PiTypeNames = list(PIDATAS) +azimuthStepSize = 0.703125 +elevationStepSize = 1.417322835 +azimuthDirections = [round(x * azimuthStepSize - 180, 3) for x in range(1, 512)] +elevationDirections = [round(x * elevationStepSize - 90, 3) for x in range(0, 127)] + def mapNearestIndex(table: list, val: float) -> int: for idx, entry in enumerate(table): @@ -592,6 +661,17 @@ def mapNearestIndex(table: list, val: float) -> int: return idx return len(table) - 1 +def mapNearestIndexDirectionAzi(table: list, val: float) -> int: + for idx, entry in enumerate(table): + if abs(entry - val) < azimuthStepSize: + return idx + return len(table) - 1 + +def mapNearestIndexDirectionElev(table: list, val: float) -> int: + for idx, entry in enumerate(table): + if abs(entry - val) < elevationStepSize: + return idx + return len(table) - 1 getListIndex = lambda mylist, val: mylist.index(val) if val in mylist else -1 @@ -923,6 +1003,157 @@ def packAudioFocus(bitstrm: BitStream, data: any): bitstrm.append(f"uint:4={level}") bitstrm.append(f"uint:4=0") +def unpackPiLatency(bitstrm: ConstBitStream, piSize: int) -> PI_LATENCY: + assert piSize == 4, "PI_LATENCY must be 4 bytes" + word = bitstrm.read(32).uint + typeBits = (word >> 27) & 0x1F + reverseType = PiTypeNames[typeBits] + raw = word & 0x07FFFFFF + # Sign-extend 27-bit + if raw & (1 << 26): + raw = raw | ~0x07FFFFFF + return PI_LATENCY(reverseType, int(raw)) + +def packPiLatency(bitstrm: BitStream, data: any) -> None: + assert type(data) == PI_LATENCY, "PI_LATENCY pack expects PI_LATENCY data" + idx = PiTypeNames.index(data.reverseType) + latency = data.latency & 0x07FFFFFF + word = (idx << 27) | latency + bitstrm.append(f"uint:32={word}") + +def unpackISMNum(bitstrm: ConstBitStream, piSize: int) -> ISM_NUM: + assert piSize == 1, "Incorrect PI Data Size for ISM_NUM" + numISM = bitstrm.read(2).uint + 1 + bitstrm.bytealign() + return ISM_NUM(num=numISM) + +def packISMNum(bitstrm: BitStream, data: any): + assert type(data) == ISM_NUM, "Data of type ISM_NUM is expected" + ism_num = cast(ISM_NUM, data) + assert ism_num.num <= 4, "Maximum 4 objects" + bitstrm.append(f'uint:2={ism_num.num-1}') + bitstrm.append(f'uint:6=0') + +def unpackISMID(bitstrm: ConstBitStream, piSize: int) -> ISM_ID: + assert piSize == 1 or piSize == 2 or piSize == 3 or piSize == 4, "Incorrect PI Data Size for ISM_ID" + IsmID = list() + for _ in range(piSize): + IsmID.append(bitstrm.read(8).uint+1) + return ISM_ID(ids=IsmID) + +def packISMID(bitstrm: BitStream, data: any): + assert type(data) == ISM_ID, "Data of type ISM_ID is expected" + ism_id = cast(ISM_ID, data) + assert len(ism_id.ids) <= 4, "Maximum 4 objects" + for id in ism_id.ids: + bitstrm.append(f'uint:8={id}') + +def unpackISMGain(bitstrm: ConstBitStream, piSize: int) -> ISM_GAIN: + assert piSize == 1 or piSize == 2 or piSize == 3 or piSize == 4, "Incorrect PI Data Size for ISM_GAIN" + IsmGain = list() + for _ in range(piSize): + IsmGain.append(bitstrm.read(6).uint) + bitstrm.bytealign() + return ISM_GAIN(gains=IsmGain) + +def packISMGain(bitstrm: BitStream, data: any): + assert type(data) == ISM_GAIN, "Data of type ISM_GAIN is expected" + ism_gain = cast(ISM_GAIN, data) + assert len(ism_gain.gains) <= 4, "Maximum 4 objects" + for gain in ism_gain.gains: + gain_idx = getListIndex(ismGains, gain) + bitstrm.append(f'uint:6={gain_idx}') + bitstrm.append(f'uint:2=0') + +def unpackISMDistanceAttenuation(bitstrm: ConstBitStream, piSize: int) -> ISM_DISTANCE_ATTENUATION: + ref_dist = None + max_dist = None + roll_off = None + assert piSize == 1*3 or piSize == 2*3 or piSize == 3*3 or piSize == 4*3, "Incorrect PI Data Size for ISM_DISTANCE_ATTENUATION" + ism_distance_attenutation = list() + for _ in range(piSize): + ref_dist = bitstrm.read(6).uint + max_dist = bitstrm.read(6).uint + roll_off = bitstrm.read(6).uint + ism_distance_attenutation.append(DISTANCE_ATTENUATION(ref_dist=ref_dist, max_dist=max_dist, roll_off=roll_off)) + bitstrm.bytealign() + return ISM_DISTANCE_ATTENUATION(distance_attenuations=ism_distance_attenutation) + +def packISMDistanceAttenuation(bitstrm: BitStream, data: any): + assert type(data) == list, "Data of type list is expected" + + for att in cast(list, data): + assert type(att) == DISTANCE_ATTENUATION, "Data of type list[DISTANCE_ATTENUATION] is expected" + ref_dist_idx = getListIndex(refDistances, att.ref_dist) + max_dist_idx = getListIndex(maxDistances, att.max_dist) + roll_off_idx = getListIndex(rolloffFactors, att.roll_off) + bitstrm.append(f'uint:6={ref_dist_idx}') + bitstrm.append(f'uint:6={max_dist_idx}') + bitstrm.append(f'uint:6={roll_off_idx}') + bitstrm.append(f'uint:6=0') + +def unpackISMDirectivity(bitstrm: ConstBitStream, piSize: int) -> list[DISTANCE_ATTENUATION]: + inner_ang = None + outer_ang = None + outer_att = None + assert piSize == 1*2 or piSize == 2*2 or piSize == 3*2 or piSize == 4*2, "Incorrect PI Data Size for ISM_DISTANCE_ATTENUATION" + directivities = list() + for _ in range(piSize): + inner_ang = bitstrm.read(5).uint + outer_ang = bitstrm.read(5).uint + outer_att = bitstrm.read(5).uint + directivities.append(DIRECTIVITY(inner_ang=inner_ang, outer_ang=outer_ang, outer_att=outer_att)) + bitstrm.bytealign() + return directivities + +def packISMDirectivity(bitstrm: BitStream, data: any): + assert type(data) == list, "Data of type ISM_DIRECTIVITY is expected" + + for dir in cast(list, data): + assert type(dir) == DIRECTIVITY, "Orientation PI Data expects a data of type list[DIRECTIVITY]" + inner_ang_idx = getListIndex(innerOuterAngles, dir.inner_ang) + outer_ang_idx = getListIndex(innerOuterAngles, dir.outer_ang) + outer_att_idx = getListIndex(outerAttenuations, dir.outer_att) + bitstrm.append(f'uint:5={inner_ang_idx}') + bitstrm.append(f'uint:5={outer_ang_idx}') + bitstrm.append(f'uint:5={outer_att_idx}') + bitstrm.append(f'uint:1=0') + +def unpackReverseISMID(bitstrm: ConstBitStream, piSize: int) -> R_ISM_ID: + assert piSize == 1, "Incorrect PI Data Size for R_ISM_ID" + id = bitstrm.read(8).uint + return R_ISM_ID(id=id) + +def packReverseISMID(bitstrm: BitStream, data: any): + assert type(data) == R_ISM_ID, "Data of type R_ISM_ID is expected" + r_ism_id = cast(R_ISM_ID, data) + bitstrm.append(f'uint:8={r_ism_id.id}') + +def unpackReverseISMGain(bitstrm: ConstBitStream, piSize: int) -> R_ISM_GAIN: + assert piSize == 1, "Incorrect PI Data Size for R_ISM_GAIN" + gain = bitstrm.read(6).uint + return R_ISM_GAIN(gain=gain) + +def packReverseISMGain(bitstrm: BitStream, data: any): + assert type(data) == R_ISM_GAIN, "Data of type R_ISM_GAIN is expected" + r_ism_gain = cast(R_ISM_GAIN, data) + gain_idx = getListIndex(ismGains, r_ism_gain.gain) + bitstrm.append(f'uint:6={gain_idx}') + bitstrm.append(f'uint:2=0') + +def unpackReverseISMDirection(bitstrm: ConstBitStream, piSize: int) -> R_ISM_DIRECTION: + assert piSize == 2, "Incorrect PI Data Size for R_ISM_DIRECTION" + azimuth = bitstrm.read(9).uint + elevation = bitstrm.read(7).uint + return R_ISM_DIRECTION(azi=azimuth, elev=elevation) + +def packReverseISMDirection(bitstrm: BitStream, data: any): + assert type(data) == R_ISM_DIRECTION, "Data of type R_ISM_DIRECTION is expected" + direction = cast(R_ISM_DIRECTION, data) + azimuth_idx = mapNearestIndexDirectionAzi(azimuthDirections, direction.azi) + elevation_idx = mapNearestIndexDirectionElev(elevationDirections, direction.elev) + bitstrm.append(f'uint:9={azimuth_idx}') + bitstrm.append(f'uint:7={elevation_idx}') PIDataUnpacker = [ unpackOrientation, # SCENE_ORIENTATION, @@ -930,13 +1161,13 @@ PIDataUnpacker = [ unpackOrientation, # DEVICE_ORIENTATION_UNCOMPENSATED unpackAcousticEnv, # ACOUSTIC_ENVIRONMENT unpackAudioDescription, # AUDIO_DESCRIPTION - unpackUnsupported, # ISM_NUM - unpackUnsupported, # ISM_ID - unpackUnsupported, # ISM_GAIN + unpackISMNum, # ISM_NUM + unpackISMID, # ISM_ID + unpackISMGain, # ISM_GAIN unpackOrientations, # ISM_ORIENTATION unpackPositions, # ISM_POSITION - unpackUnsupported, # ISM_DISTANCE_ATTENUATION - unpackUnsupported, # ISM_DIRECTIVITY + unpackISMDistanceAttenuation, # ISM_DISTANCE_ATTENUATION + unpackISMDirectivity, # ISM_DIRECTIVITY unpackDiegetic, # DIEGETIC_TYPE unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION_INDICATION unpackAudioFocus, # AUDIO_FOCUS_INDICATION @@ -946,12 +1177,12 @@ PIDataUnpacker = [ unpackPosition, # LISTENER_POSITION unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION_REQUEST unpackAudioFocus, # AUDIO_FOCUS_REQUEST - unpackUnsupported, # PI_LATENCY - unpackUnsupported, # R_ISM_ID - unpackUnsupported, # R_ISM_GAIN + unpackPiLatency, # PI_LATENCY + unpackReverseISMID, # R_ISM_ID + unpackReverseISMGain, # R_ISM_GAIN unpackOrientation, # R_ISM_ORIENTATION unpackPosition, # R_ISM_POSITION - unpackUnsupported, # R_ISM_DIRECTION + unpackReverseISMDirection, # R_ISM_DIRECTION unpackUnsupported, # RESERVED27 unpackUnsupported, # RESERVED28 unpackUnsupported, # RESERVED29 @@ -965,13 +1196,13 @@ PIDataPacker = [ packOrientation, # DEVICE_ORIENTATION_UNCOMPENSATED packAcousticEnv, # ACOUSTIC_ENVIRONMENT packAudioDescription, # AUDIO_DESCRIPTION - packUnsupported, # ISM_NUM - packUnsupported, # ISM_ID - packUnsupported, # ISM_GAIN + packISMNum, # ISM_NUM + packISMID, # ISM_ID + packISMGain, # ISM_GAIN packOrientations, # ISM_ORIENTATION packPositions, # ISM_POSITION - packUnsupported, # ISM_DISTANCE_ATTENUATION - packUnsupported, # ISM_DIRECTIVITY + packISMDistanceAttenuation, # ISM_DISTANCE_ATTENUATION + packISMDirectivity, # ISM_DIRECTIVITY packDiegetic, # DIEGETIC_TYPE packDAS, # DYNAMIC_AUDIO_SUPPRESSION_INDICATION packAudioFocus, # AUDIO_FOCUS_INDICATION @@ -981,12 +1212,12 @@ PIDataPacker = [ packPosition, # LISTENER_POSITION packDAS, # DYNAMIC_AUDIO_SUPPRESSION_REQUEST packAudioFocus, # AUDIO_FOCUS_DIRECTION - packUnsupported, # PI_LATENCY - packUnsupported, # R_ISM_ID - packUnsupported, # R_ISM_GAIN + packPiLatency, # PI_LATENCY + packReverseISMID, # R_ISM_ID + packReverseISMGain, # R_ISM_GAIN packOrientation, # R_ISM_ORIENTATION packPosition, # R_ISM_POSITION - packUnsupported, # R_ISM_DIRECTION + packReverseISMDirection, # R_ISM_DIRECTION packUnsupported, # RESERVED27 packUnsupported, # RESERVED28 packUnsupported, # RESERVED29 diff --git a/tests/rtp/test_format_switching.py b/tests/rtp/test_format_switching.py new file mode 100644 index 0000000000..43daaa3da7 --- /dev/null +++ b/tests/rtp/test_format_switching.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 + +__copyright__ = """ +(C) 2022-2025 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. +""" + +__doc__ = """ +To configure test modules. +""" + +import pytest +import csv +import os +import sys +import random + +from tempfile import TemporaryDirectory +from pathlib import Path +import soundfile as sf +import numpy as np + +ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) +sys.path.append(ROOT_DIR) + +from tests.conftest import EncoderFrontend, DecoderFrontend +from tests.create_short_testvectors import create_short_testvectors +from tests.renderer.constants import ( + FORMAT_TO_FILE_COMPARETEST, + FORMAT_TO_METADATA_FILES, +) +from ivasrtp import * +import platform + +BIN_EXT = ".exe" if platform.system() == "Windows" else "" + +FORMAT_ARGUMENT_MAPPING = { + "MONO": [], + "STEREO": ["-stereo"], + "5_1": ["-mc", "5_1"], + "7_1": ["-mc", "7_1"], + "5_1_2": ["-mc", "5_1_2"], + "5_1_4": ["-mc", "5_1_4"], + "7_1_4": ["-mc", "7_1_4"], + "FOA": ["-sba", "+1"], + "HOA2": ["-sba", "+2"], + "HOA3": ["-sba", "+3"], + "ISM1": ["-ism", "1"], + "ISM2": ["-ism", "2"], + "ISM3": ["-ism", "3"], + "ISM4": ["-ism", "4"], + "MASA1": ["-masa", "1"], + "MASA2": ["-masa", "2"], + "ISM1MASA1": ["-ism_masa", "1", "1"], + "ISM2MASA1": ["-ism_masa", "2", "1"], + "ISM3MASA1": ["-ism_masa", "3", "1"], + "ISM4MASA1": ["-ism_masa", "4", "1"], + "ISM1MASA2": ["-ism_masa", "1", "2"], + "ISM2MASA2": ["-ism_masa", "2", "2"], + "ISM3MASA2": ["-ism_masa", "3", "2"], + "ISM4MASA2": ["-ism_masa", "4", "2"], + "ISM1SBA1": ["-ism_sba", "1", "+1"], + "ISM1SBA2": ["-ism_sba", "1", "+2"], + "ISM1SBA3": ["-ism_sba", "1", "+3"], + "ISM2SBA1": ["-ism_sba", "2", "+1"], + "ISM2SBA2": ["-ism_sba", "2", "+2"], + "ISM2SBA3": ["-ism_sba", "2", "+3"], + "ISM3SBA1": ["-ism_sba", "3", "+1"], + "ISM3SBA2": ["-ism_sba", "3", "+2"], + "ISM3SBA3": ["-ism_sba", "3", "+3"], + "ISM4SBA1": ["-ism_sba", "4", "+1"], + "ISM4SBA2": ["-ism_sba", "4", "+2"], + "ISM4SBA3": ["-ism_sba", "4", "+3"], +} + +def test_format_switching_new_encoder ( + record_property, + dut_decoder_frontend: DecoderFrontend +): + bitrate = 48000 + fs = 48 + bandwidth = "FB" + outMode = "" + + dut_encoder_path = Path(ROOT_DIR).joinpath(f"IVAS_cod{BIN_EXT}") + dut_encoder_fmtsw_path = Path(ROOT_DIR).joinpath(f"IVAS_cod_fmtsw{BIN_EXT}") + dut_encoder_fmtsw_frontend = EncoderFrontend( + dut_encoder_fmtsw_path, "DUT", record_property + ) + + # Create 1s test files + cut_suffix = "_cut.wav" + test_file = Path( str(FORMAT_TO_FILE_COMPARETEST["MONO"]).replace(".wav", cut_suffix) ) + if not test_file.exists(): + create_short_testvectors(1.0, False, None) + + with TemporaryDirectory() as tmp_dir: + + # Create file for encoding commands + temp_format_switching_file = Path(tmp_dir).joinpath("format_switching_input.txt").absolute() + temp_rtpdump = Path(tmp_dir).joinpath("output_concatenated.rtpdump").absolute() + with open (temp_format_switching_file, mode="a") as outFile: + for key, audioFile in FORMAT_TO_FILE_COMPARETEST.items(): + if key in ["META", "16ch_8+4+4", "4d4", "t_design_4"]: + break + + encoder_args = [] + encoder_args += [str(dut_encoder_path)] + encoder_args += ["-rtpdump", "1"] + encoder_args += FORMAT_ARGUMENT_MAPPING[key] + if key in FORMAT_TO_METADATA_FILES: + encoder_args += FORMAT_TO_METADATA_FILES[key] + elif "SBA" in key: + # ISM metadata files for OSBA + encoder_args += FORMAT_TO_METADATA_FILES[key[:4]] + encoder_args += [str(bitrate)] + encoder_args += [str(fs)] + cutFile = Path( str(audioFile).replace(".wav", cut_suffix) ) + encoder_args += [str(cutFile)] + encoder_args += [str(temp_rtpdump)] + + outFile.write(' '.join(encoder_args)) + outFile.write("\n") + + # Encode with the format switch encoder + dut_encoder_fmtsw_frontend.run( + bitrate='', + input_sampling_rate='', + input_path=temp_format_switching_file, + output_bitstream_path='', + quiet_mode=False, + fmtsw_command=True + ) + + # Decode the combined bitstreams + cat_output_rtpdump = Path(tmp_dir).joinpath(f"output_concatenated.wav").absolute() + + dut_decoder_frontend.run( + output_config=outMode, + output_sampling_rate=48, + input_bitstream_path=temp_rtpdump, + output_path=cat_output_rtpdump, + add_option_list= ["-VOIP_HF_ONLY=1"] + ) diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 44d08a91e1..53cb6488c5 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -61,6 +61,7 @@ from tempfile import TemporaryDirectory from pathlib import Path from ivasrtp import * import numpy as np +import json from pyaudio3dtools.audiofile import readfile ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) @@ -227,6 +228,59 @@ def generatePiData(startTs: int, endTs: int) -> dict: ) someAuFocusLvl = lambda: AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] + someLatency = lambda: PI_LATENCY( + reverseType=random.choice([ + "PLAYBACK_DEVICE_ORIENTATION", + "HEAD_ORIENTATION", + "LISTENER_POSITION", + "DYNAMIC_AUDIO_SUPPRESSION_REQUEST", + "AUDIO_FOCUS_REQUEST" + ]), + latency=random.randint(- (1 << 26), (1 << 26) - 1) + ) + someAcousticEnvAEID = lambda: ACOUSTIC_ENVIRONMENT( + aeid=random.randint(0, 127) + ) + someAcousticEnvLR = lambda: ACOUSTIC_ENVIRONMENT( + aeid=random.randint(0, 127), + rt60=(float(rt60Value[random.randint(0, len(rt60Value) - 1)]), + float(rt60Value[random.randint(0, len(rt60Value) - 1)]), + float(rt60Value[random.randint(0, len(rt60Value) - 1)])), + dsr=(float(dsrValue[random.randint(0, len(dsrValue) - 1)]), + float(dsrValue[random.randint(0, len(dsrValue) - 1)]), + float(dsrValue[random.randint(0, len(dsrValue) - 1)])), + ) + someAcousticEnvERLR = lambda: ACOUSTIC_ENVIRONMENT( + aeid=random.randint(0, 127), + rt60=(float(rt60Value[random.randint(0, len(rt60Value) - 1)]), + float(rt60Value[random.randint(0, len(rt60Value) - 1)]), + float(rt60Value[random.randint(0, len(rt60Value) - 1)])), + dsr=(float(dsrValue[random.randint(0, len(dsrValue) - 1)]), + float(dsrValue[random.randint(0, len(dsrValue) - 1)]), + float(dsrValue[random.randint(0, len(dsrValue) - 1)])), + dim=(float(roomDimensionValue[random.randint(0, len(roomDimensionValue) - 1)]), + float(roomDimensionValue[random.randint(0, len(roomDimensionValue) - 1)]), + float(roomDimensionValue[random.randint(0, len(roomDimensionValue) - 1)])), + abscoeff=(float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)]), + float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)]), + float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)]), + float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)]), + float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)]), + float(absorptionCoeffValues[random.randint(0, len(absorptionCoeffValues) - 1)])), + ) + someAcousticEnvList = [someAcousticEnvAEID, someAcousticEnvLR, someAcousticEnvERLR] + + someNumISM = lambda : ISM_NUM(num=random.randint(1, 4)) + someISMIds = lambda num_ism : ISM_ID(ids=[int(random.getrandbits(8)) for _ in range(num_ism)]) + someISMGains = lambda num_ism : ISM_GAIN(gains=[random.choice([int(random.randint(-24,12)), -128]) for _ in range(num_ism)]) # -128 corresponds to -Inf + someISMOrientations = lambda num_ism : [ORIENTATION(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0) for _ in range(num_ism)] + someISMPositions = lambda num_ism : [POSITION( x=random.randint(-32788, 32767)/100.0, y=random.randint(-32788, 32767)/100.0, z=random.randint(-32788, 32767)/100.0) for _ in range(num_ism)] + someISMDistanceAttenuations = lambda num_ism : [DISTANCE_ATTENUATION(ref_dist=random.randint(1,64)/10.0, max_dist=random.randint(1,64), roll_off=random.randint(0,40)/10.0) for _ in range(num_ism)] + someISMDirectivities = lambda num_ism : [DIRECTIVITY(inner_ang=random.randint(0,24)*15, outer_ang=random.randint(0,24)*15, outer_att=random.choice([random.randint(-30,0)*3, -128])) for _ in range(num_ism)] # -128 corresponds to -Inf + + someReverseISMId = lambda : R_ISM_ID(id=(random.getrandbits(8))) + someReverseISMGain = lambda : R_ISM_GAIN(gain=random.choice([int(random.randint(-24,12)), -128])) + someReverseISMDirection = lambda : R_ISM_DIRECTION( azi=random.randint(1,512) * azimuthStepSize - 180.0, elev=random.randint(0,127) * elevationStepSize - 90.0 ) for ts in range(startTs, endTs, 320): pidata = dict() @@ -240,9 +294,21 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["DYNAMIC_AUDIO_SUPPRESSION_REQUEST"] = someDAS() pidata["AUDIO_DESCRIPTION"] = [someDesc() for n in range(random.randint(1, 5))] pidata["DIEGETIC_TYPE"] = someDIG() - pidata["ACOUSTIC_ENVIRONMENT"] = ACOUSTIC_ENVIRONMENT( - aeid=random.randint(0, 127) - ) + pidata["ACOUSTIC_ENVIRONMENT"] = random.choice(someAcousticEnvList)() + pidata["PI_LATENCY"] = someLatency() + pidata["ISM_NUM"] = someNumISM() + pidata["ISM_ID"] = someISMIds(pidata["ISM_NUM"].num) + pidata["ISM_GAIN"] = someISMGains(pidata["ISM_NUM"].num) + pidata["ISM_ORIENTATION"] = someISMOrientations(pidata["ISM_NUM"].num) + pidata["ISM_POSITION"] = someISMPositions(pidata["ISM_NUM"].num) + pidata["ISM_DISTANCE_ATTENUATION"] = someISMDistanceAttenuations(pidata["ISM_NUM"].num) + pidata["ISM_DIRECTIVITY"] = someISMDirectivities(pidata["ISM_NUM"].num) + + pidata["R_ISM_ID"] = someReverseISMId() + pidata["R_ISM_GAIN"] = someReverseISMGain() + pidata["R_ISM_ORIENTATION"] = someOrientation() + pidata["R_ISM_POSITION"] = somePosition() + pidata["R_ISM_DIRECTION"] = someReverseISMDirection() data[str(ts)] = pidata return data @@ -311,7 +377,13 @@ def isEqualAcousticEnv(ref: ACOUSTIC_ENVIRONMENT, dut: ACOUSTIC_ENVIRONMENT): dut.abscoeff ), "Acoustic Env PI Data mismatch in len(abscoeff)" for r, d in zip(ref.rt60, dut.rt60): - assert r == d, f"Acoustic Env PI Data mismatch in rt60 {r} != {d}" + assert abs(r - d) < 0.0001, f"Acoustic Env PI Data mismatch in RT60 {r} != {d}" + for r, d in zip(ref.dsr, dut.dsr): + assert abs(r - d) < 0.0001, f"Acoustic Env PI Data mismatch in DSR {r} != {d}" + for r, d in zip(ref.dim, dut.dim): + assert abs(r - d) < 0.0001, f"Acoustic Env PI Data mismatch in room dimension {r} != {d}" + for r, d in zip(ref.abscoeff, dut.abscoeff): + assert abs(r - d) < 0.0001, f"Acoustic Env PI Data mismatch in absorption coefficient {r} != {d}" def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): @@ -333,6 +405,43 @@ def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): ), "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" +def isEqualPILatency(ref: PI_LATENCY, dut: PI_LATENCY): + assert ref.reverseType == dut.reverseType, \ + f"PI_LATENCY type mismatch: {dut.reverseType} != {ref.reverseType}" + assert ref.latency == dut.latency, \ + f"PI_LATENCY latency mismatch: {dut.latency} != {ref.latency}" + +def isEqualISMNum(ref: ISM_NUM, dut: ISM_NUM): + assert ref.num == dut.num, "ISM NUM PI Data mismatch" + +def isEqualISMID(ref: ISM_ID, dut: ISM_ID): + for r, d in zip(ref.ids, dut.ids): + assert r == d, f"ISM ID PI Data mismatch {r} != {d}" + +def isEqualISMGain(ref: ISM_GAIN, dut: ISM_GAIN): + for r, d in zip(ref.gains, dut.gains): + assert r == d, f"ISM GAIN PI Data mismatch {r} != {d}" + +def isEqualISMDistanceAttenuation(ref: DISTANCE_ATTENUATION, dut: DISTANCE_ATTENUATION): + assert abs(ref.ref_dist - dut.ref_dist) < 0.0001, "DISTANCE ATTENUATION PI Data mismatch in ref_dist" + assert abs(ref.max_dist - dut.max_dist) < 0.0001, "DISTANCE ATTENUATION PI Data mismatch in max_dist" + assert abs(ref.roll_off - dut.roll_off) < 0.0001, "DISTANCE ATTENUATION PI Data mismatch in roll_off" + +def isEqualISMDirectivity(ref: DIRECTIVITY, dut: DIRECTIVITY): + assert ref.inner_ang == dut.inner_ang, "DIRECTIVITYPI Data mismatch in inner_ang" + assert ref.outer_ang == dut.outer_ang, "DIRECTIVITY PI Data mismatch in outer_ang" + assert abs(ref.outer_att - dut.outer_att) < 0.0001, "DIRECTIVITY PI Data mismatch in outer_att" + +def isEqualReverseISMID(ref: R_ISM_ID, dut: R_ISM_ID): + assert ref.id == dut.id, f"Reverse ISM ID PI Data mismatch {ref.id} != {dut.id}" + +def isEqualReverseISMGain(ref: R_ISM_GAIN, dut: R_ISM_GAIN): + assert ref.gain == dut.gain, f"Reverse ISM GAIN PI Data mismatch {ref.gain} != {dut.gain}" + +def isEqualReverseISMDirection(ref: R_ISM_DIRECTION, dut: R_ISM_DIRECTION): + assert abs(ref.azi - dut.azi) < azimuthStepSize + 0.1, "Reverse ISM Direction PI Data mismatch in azimuth (more than one index difference)" + assert abs(ref.elev - dut.elev) < elevationStepSize + 0.1, "Reverse ISM Direction PI Data mismatch in elevation (more than one index difference)" + class CSVREADER: def __init__(self, csvFile: Path): @@ -632,11 +741,37 @@ def run_rtp_bitstream_tests( isEqualAcousticEnv(ACOUSTIC_ENVIRONMENT(**decoded), data) elif type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: isEqualAudioFocus(AUDIO_FOCUS(**decoded), data) + elif type(generatedPIData[ts][pitype]) == PI_LATENCY: + isEqualPILatency(PI_LATENCY(**decoded), data) elif type(generatedPIData[ts][pitype]) == list: - for r, d in zip( - generatedPIData[ts][pitype], decodedPiData[ts][pitype] - ): - isEqualAD(AUDIO_DESCRIPTION(**d), r) + if pitype == "AUDIO_DESCRIPTION": + for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + isEqualAD(AUDIO_DESCRIPTION(**d), r) + elif pitype == "ISM_ORIENTATION": + for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + isEqualOrientation(ORIENTATION(**d), r) + elif pitype == "ISM_POSITION": + for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + isEqualPosition(POSITION(**d), r) + elif pitype == "ISM_DISTANCE_ATTENUATION": + for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + isEqualISMDistanceAttenuation(DISTANCE_ATTENUATION(**d), r) + elif pitype == "ISM_DIRECTIVITY": + for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + isEqualISMDirectivity(DIRECTIVITY(**d), r) + elif type(generatedPIData[ts][pitype]) == ISM_NUM: + isEqualISMNum(ISM_NUM(**decoded), data) + elif type(generatedPIData[ts][pitype]) == ISM_ID: + isEqualISMID(ISM_ID(**decoded), data) + elif type(generatedPIData[ts][pitype]) == ISM_GAIN: + isEqualISMGain(ISM_GAIN(**decoded), data) + elif type(generatedPIData[ts][pitype]) == R_ISM_ID: + isEqualReverseISMID(R_ISM_ID(**decoded), data) + elif type(generatedPIData[ts][pitype]) == R_ISM_GAIN: + isEqualReverseISMGain(R_ISM_GAIN(**decoded), data) + elif type(generatedPIData[ts][pitype]) == R_ISM_DIRECTION: + isEqualReverseISMDirection(R_ISM_DIRECTION(**decoded), data) + else: assert False, "Unsupported PI data found" diff --git a/tests/split_rendering/constants.py b/tests/split_rendering/constants.py index 5869ec3b78..7d58a03b0c 100644 --- a/tests/split_rendering/constants.py +++ b/tests/split_rendering/constants.py @@ -1,46 +1,37 @@ #!/usr/bin/env python3 """ - (C) 2022-2025 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. +(C) 2022-2025 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. """ -from pathlib import Path import platform - -from tests.renderer.constants import ( - BIN_SUFFIX_MERGETARGET, - CUSTOM_LS_TO_TEST, - FORMAT_TO_FILE_COMPARETEST, - FORMAT_TO_FILE_SMOKETEST, - FORMAT_TO_METADATA_FILES, - METADATA_SCENES_TO_TEST, -) +from pathlib import Path if platform.system() == "Windows": EXE_SUFFIX = ".exe" @@ -52,7 +43,9 @@ else: """ Set up paths """ TESTS_DIR = Path(__file__).parent RENDER_CFG_DIR = TESTS_DIR.joinpath("renderer_configs").resolve() -RENDER_FRAMING_CFG_DIR = TESTS_DIR.joinpath("renderer_configs").joinpath("framing").resolve() +RENDER_FRAMING_CFG_DIR = ( + TESTS_DIR.joinpath("renderer_configs").joinpath("framing").resolve() +) ERROR_PATTERNS_DIR = TESTS_DIR.joinpath("error_patterns").resolve() OUTPUT_PATH_REF = TESTS_DIR.joinpath("ref") @@ -63,6 +56,14 @@ CUSTOM_LAYOUT_DIR = SCRIPTS_DIR.joinpath("ls_layouts") HR_TRAJECTORY_DIR = SCRIPTS_DIR.joinpath("trajectories") TESTV_DIR = SCRIPTS_DIR.joinpath("testv") +if platform.system() == "Windows": + TOOLS_DIR = SCRIPTS_DIR / "tools" / "Win32" +elif platform.system() == "Linux": + TOOLS_DIR = SCRIPTS_DIR / "tools"/ "Linux" +elif platform.system() == "Darwin": + TOOLS_DIR = SCRIPTS_DIR / "tools" / "Darwin" +else: + assert False, "Unsupported platform" """ Renderer configurations """ RENDERER_CONFIGS_DEFAULT_CODEC = [ @@ -100,11 +101,13 @@ RENDERER_CONFIGS_TO_TEST_OMASA = ( RENDERER_CONFIGS_TO_TEST_OSBA = ( RENDERER_CONFIGS_DEFAULT_CODEC + RENDERER_CONFIGS_LC3PLUS_CODEC ) -RENDERER_CONFIGS_TO_TEST_PLC = RENDERER_CONFIGS_FASTCONV_RENDERER + RENDERER_CONFIGS_LC3PLUS_CODEC +RENDERER_CONFIGS_TO_TEST_PLC = ( + RENDERER_CONFIGS_FASTCONV_RENDERER + RENDERER_CONFIGS_LC3PLUS_CODEC +) """ Trajectories """ SPLIT_REND_HR_TRAJECTORIES_TO_TEST = [ - "rotate_euler_quaternion_5s", + "rotate_euler_quaternion_30s", ] """ IVAS specific constants """ @@ -125,43 +128,48 @@ FORMAT_TO_IVAS_COD_FORMAT = { "HOA3": ["-sba", "3"], "MASA1": ["-masa", "1"], "MASA2": ["-masa", "2"], - "OMASA_1_1": ["-ism_masa", "1", "1"], - "OMASA_1_2": ["-ism_masa", "2", "1"], - "OMASA_1_3": ["-ism_masa", "3", "1"], - "OMASA_1_4": ["-ism_masa", "4", "1"], - "OMASA_2_1": ["-ism_masa", "1", "2"], - "OMASA_2_2": ["-ism_masa", "2", "2"], - "OMASA_2_3": ["-ism_masa", "3", "2"], - "OMASA_2_4": ["-ism_masa", "4", "2"], - "OSBA_1_1": ["-ism_sba", "1", "1"], - "OSBA_1_2": ["-ism_sba", "1", "2"], - "OSBA_1_3": ["-ism_sba", "1", "3"], - "OSBA_2_1": ["-ism_sba", "2", "1"], - "OSBA_2_2": ["-ism_sba", "2", "2"], - "OSBA_2_3": ["-ism_sba", "2", "3"], - "OSBA_3_1": ["-ism_sba", "3", "1"], - "OSBA_3_2": ["-ism_sba", "3", "2"], - "OSBA_3_3": ["-ism_sba", "3", "3"], - "OSBA_4_1": ["-ism_sba", "4", "1"], - "OSBA_4_2": ["-ism_sba", "4", "2"], - "OSBA_4_3": ["-ism_sba", "4", "3"], + "ISM1MASA1": ["-ism_masa", "1", "1"], + "ISM2MASA1": ["-ism_masa", "2", "1"], + "ISM3MASA1": ["-ism_masa", "3", "1"], + "ISM4MASA1": ["-ism_masa", "4", "1"], + "ISM1MASA2": ["-ism_masa", "1", "2"], + "ISM2MASA2": ["-ism_masa", "2", "2"], + "ISM3MASA2": ["-ism_masa", "3", "2"], + "ISM4MASA2": ["-ism_masa", "4", "2"], + "ISM1SBA1": ["-ism_sba", "1", "1"], + "ISM1SBA2": ["-ism_sba", "1", "2"], + "ISM1SBA3": ["-ism_sba", "1", "3"], + "ISM2SBA1": ["-ism_sba", "2", "1"], + "ISM2SBA2": ["-ism_sba", "2", "2"], + "ISM2SBA3": ["-ism_sba", "2", "3"], + "ISM3SBA1": ["-ism_sba", "3", "1"], + "ISM3SBA2": ["-ism_sba", "3", "2"], + "ISM3SBA3": ["-ism_sba", "3", "3"], + "ISM4SBA1": ["-ism_sba", "4", "1"], + "ISM4SBA2": ["-ism_sba", "4", "2"], + "ISM4SBA3": ["-ism_sba", "4", "3"], } INPUT_FORMATS_AMBI_SPLIT_REND = ["FOA", "HOA3"] INPUT_FORMATS_MC_SPLIT_REND = ["5_1", "7_1_4"] INPUT_FORMATS_ISM_SPLIT_REND = ["ISM4"] INPUT_FORMATS_MASA_SPLIT_REND = ["MASA1", "MASA2"] -INPUT_FORMATS_OMASA_SPLIT_REND = ["OMASA_1_1", "OMASA_2_4"] # number of TCs in MASA, number of ISM objects +INPUT_FORMATS_OMASA_SPLIT_REND = ["ISM1MASA1", "ISM4MASA2"] INPUT_FORMATS_OSBA_SPLIT_REND = [ - "OSBA_1_1", - "OSBA_4_3", -] # number of ISM objects, then SBA order + "ISM1SBA1", + "ISM4SBA3", +] IVAS_BITRATES_AMBI = ["80000", "512000"] IVAS_BITRATES_MC = ["128000", "160000", "384000"] IVAS_BITRATES_ISM = ["128000"] IVAS_BITRATES_MASA = ["24400", "128000"] -IVAS_BITRATES_OMASA = ["16400", "32000", "96000", "384000"] # test all underlying coding modes (here, for 4 ISM) +IVAS_BITRATES_OMASA = [ + "16400", + "32000", + "96000", + "384000", +] # test all underlying coding modes (here, for 4 ISM) IVAS_BITRATES_OSBA = ["256000", "512000"] IVAS_MAX_ISM_BITRATE = { @@ -180,6 +188,9 @@ INPUT_DURATION_SEC = 5 """ PLC constants """ PLC_ERROR_PATTERNS = [str(ep.stem) for ep in ERROR_PATTERNS_DIR.glob("*.ep")] +""" Delay profiles for testing VoIP mode """ +DELAY_PROFILES = [None, "dly_error_profile_5"] + """ Encoder commandline template """ SPLIT_PRE_COD_CMD = [ str(TESTS_DIR.parent.parent.joinpath("IVAS_cod")), diff --git a/tests/split_rendering/test_split_rendering.py b/tests/split_rendering/test_split_rendering.py index a881a7ad23..32efd73109 100644 --- a/tests/split_rendering/test_split_rendering.py +++ b/tests/split_rendering/test_split_rendering.py @@ -1,44 +1,42 @@ #!/usr/bin/env python3 """ - (C) 2022-2025 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. +(C) 2022-2025 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.split_rendering.utils import * - """ Ambisonics """ - - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_AMBI) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_AMBI) @@ -47,11 +45,16 @@ def test_ambisonics_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, - test_info, in_fmt, bitrate, render_config, trajectory + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, ): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") @@ -67,14 +70,14 @@ def test_ambisonics_full_chain_split( pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) - @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_AMBI) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_AMBI_SPLIT_REND) @@ -82,34 +85,39 @@ def test_ambisonics_external_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, - test_info, in_fmt, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) """ Multichannel """ - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_MC) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_MC) @@ -118,18 +126,23 @@ def test_multichannel_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, - test_info, in_fmt, bitrate, render_config, trajectory + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, ): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -138,14 +151,14 @@ def test_multichannel_full_chain_split( pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) - @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_MC) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MC_SPLIT_REND) @@ -153,34 +166,39 @@ def test_multichannel_external_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) """ ISM """ - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_ISM) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_ISM) @@ -189,17 +207,23 @@ def test_ism_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, bitrate, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -208,14 +232,14 @@ test_info, in_fmt, bitrate, render_config, trajectory): pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) - @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_ISM) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_ISM_SPLIT_REND) @@ -223,34 +247,39 @@ def test_ism_external_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) """ MASA """ - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_MASA) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_MASA) @@ -259,17 +288,23 @@ def test_masa_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, bitrate, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -278,14 +313,14 @@ test_info, in_fmt, bitrate, render_config, trajectory): pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) - @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_MASA) @pytest.mark.parametrize("in_fmt", INPUT_FORMATS_MASA_SPLIT_REND) @@ -293,34 +328,39 @@ def test_masa_external_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) """ OMASA """ - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OMASA) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_OMASA) @@ -329,17 +369,23 @@ def test_omasa_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, bitrate, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -348,17 +394,54 @@ test_info, in_fmt, bitrate, render_config, trajectory): pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) -""" OSBA """ +@pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OMASA) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OMASA_SPLIT_REND) +def test_omasa_external_split( + record_property, + props_to_record, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): + post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") + pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") + run_external_split_rendering( + record_property, + props_to_record, + test_info, + in_fmt=in_fmt, + render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, + pre_trajectory=pre_trajectory, + post_trajectory=post_trajectory, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + ) +""" OSBA """ + + +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OSBA) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_OSBA) @@ -370,14 +453,20 @@ def test_osba_full_chain_split( get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, bitrate, render_config, trajectory): + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + trajectory, + delay_profile, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -386,15 +475,51 @@ test_info, in_fmt, bitrate, render_config, trajectory): pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) -""" PLC """ +@pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) +@pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OSBA) +@pytest.mark.parametrize("in_fmt", INPUT_FORMATS_OSBA_SPLIT_REND) +def test_osba_external_split( + record_property, + props_to_record, + get_mld, + get_mld_lim, + get_ssnr, + get_odg, + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, +): + post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") + pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") + run_external_split_rendering( + record_property, + props_to_record, + test_info, + in_fmt=in_fmt, + render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, + pre_trajectory=pre_trajectory, + post_trajectory=post_trajectory, + get_mld=get_mld, + mld_lim=get_mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + ) + + +""" PLC """ @pytest.mark.parametrize("error_pattern", PLC_ERROR_PATTERNS) @@ -405,61 +530,71 @@ def test_post_rend_plc( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config, trajectory, error_pattern): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, + error_pattern, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), + binary_suffix=EXE_SUFFIX, pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, plc_error_pattern=ERROR_PATTERNS_DIR.joinpath(f"{error_pattern}.ep"), get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) """ BINAURAL_SPLIT_PCM """ - full_chain_split_pcm_params = [ ("HOA3", "96000", "split_renderer_config_1dof_512k_default"), ("7_1_4", "512000", "split_renderer_config_3dofhq_512k_lc3plus"), ("ISM4", "384000", "split_renderer_config_2dof_768k_default"), ("MASA2", "256000", "split_renderer_config_3dof_384k_lcld"), - ("OMASA_2_4", "256000", "split_renderer_config_3dof_384k_lcld"), + ("ISM4MASA2", "256000", "split_renderer_config_3dof_384k_lcld"), ] - +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("in_fmt,bitrate,render_config", full_chain_split_pcm_params) def test_full_chain_split_pcm( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, bitrate, render_config): + get_odg_bin, + test_info, + in_fmt, + bitrate, + render_config, + delay_profile, +): trajectory = SPLIT_REND_HR_TRAJECTORIES_TO_TEST[0] post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, bitrate=bitrate, @@ -469,10 +604,11 @@ test_info, in_fmt, bitrate, render_config): post_trajectory=post_trajectory, renderer_fmt="BINAURAL_SPLIT_PCM", get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) @@ -483,35 +619,38 @@ external_split_pcm_params = [ ] - @pytest.mark.parametrize("in_fmt,render_config", external_split_pcm_params) def test_external_split_pcm( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config): + get_odg_bin, + test_info, + in_fmt, + render_config, +): trajectory = SPLIT_REND_HR_TRAJECTORIES_TO_TEST[0] post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_CFG_DIR.joinpath(f"{render_config}.txt"), pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, + binary_suffix=EXE_SUFFIX, renderer_fmt="BINAURAL_SPLIT_PCM", get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) @@ -524,52 +663,67 @@ def test_framing_combinations_external_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, -test_info, in_fmt, render_config, trajectory, post_rend_fr, pre_rend_fr): + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, + post_rend_fr, + pre_rend_fr, +): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_external_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_FRAMING_CFG_DIR.joinpath(f"{render_config}.txt"), pre_trajectory=pre_trajectory, post_trajectory=post_trajectory, + binary_suffix=EXE_SUFFIX, post_rend_fr=post_rend_fr, pre_rend_fr=pre_rend_fr, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, ) + +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_FRAMING) -@pytest.mark.parametrize("in_fmt", ["5_1"]) +@pytest.mark.parametrize("in_fmt", ["5_1", "FOA"]) @pytest.mark.parametrize("pre_rend_fr", SPLIT_RENDERER_PRE_FRAMINGS) @pytest.mark.parametrize("post_rend_fr", SPLIT_RENDERER_POST_FRAMINGS) def test_framing_combinations_full_chain_split( record_property, props_to_record, get_mld, - get_mld_lim, + get_mld_lim, get_ssnr, get_odg, - get_odg_bin, - test_info, in_fmt, render_config, trajectory, post_rend_fr, pre_rend_fr + get_odg_bin, + test_info, + in_fmt, + render_config, + trajectory, + post_rend_fr, + pre_rend_fr, + delay_profile, ): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") run_full_chain_split_rendering( - record_property, - props_to_record, + record_property, + props_to_record, test_info, in_fmt=in_fmt, render_config=RENDER_FRAMING_CFG_DIR.joinpath(f"{render_config}.txt"), @@ -580,8 +734,9 @@ def test_framing_combinations_full_chain_split( post_rend_fr=post_rend_fr, pre_rend_fr=pre_rend_fr, get_mld=get_mld, - mld_lim=get_mld_lim, + mld_lim=get_mld_lim, get_ssnr=get_ssnr, get_odg=get_odg, - get_odg_bin=get_odg_bin, + get_odg_bin=get_odg_bin, + delay_profile=SCRIPTS_DIR / "dly_error_profiles" / f"{delay_profile}.dat" if delay_profile else None, ) diff --git a/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py b/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py new file mode 100644 index 0000000000..d70640b33a --- /dev/null +++ b/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 + +""" + (C) 2022-2025 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 tempfile import TemporaryDirectory +from pathlib import Path +import filecmp + +from tests.split_rendering.utils import * +from tests.split_rendering.constants import SCRIPTS_DIR, TESTV_DIR +from tests.test_be_for_jbm_neutral_dly_profile import ( + INPUT_FILES, + get_options_cod, +) +from pyaudio3dtools import audioarray, audiofile + +IN_FORMATS = [ + "MC_5_1", + "ISM4", + "FOA", + "MASA2TC", +] + +DELAY_PROFILES = ["dly_error_profile_0.dat", "dly_error_profile_5.dat"] + + +# Compares PCM output and tracefile from a VoIP BINAURAL_SPLIT_PCM chain with equivalent BINAURAL +# chain to ensure time-scaling and other JBM operations are BE between the two. +@pytest.mark.parametrize("in_format", IN_FORMATS) +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) +def test_voip_be_splitrend_vs_binaural( + in_format, + delay_profile, + dut_encoder_frontend, + dut_decoder_frontend, + ivas_bitrate=128000, +): + with TemporaryDirectory() as tmp_dir: + tmp_dir = Path(tmp_dir) + + sampling_rate_khz = 48 + delay_profile_path = SCRIPTS_DIR / "dly_error_profiles" / delay_profile + delay_profile_id = int(delay_profile[-5]) + + # run encoder + bitstream_file = (tmp_dir / f"{in_format}-dly{delay_profile_id}.192").absolute() + dtx = False + wav_in = TESTV_DIR / INPUT_FILES[in_format] + dut_encoder_frontend.run( + ivas_bitrate, + sampling_rate_khz, + wav_in, + bitstream_file, + add_option_list=get_options_cod(in_format, dtx), + run_dir=tmp_dir, + ) + + def run_decoder(out_format): + options = [] + + # With CLDFB pose correction (default with BINAURAL_SPLIT_PCM), a 20 ms audio frame is + # rendered with only one head position (first of the 4 per frame). If we want to compare + # the output from BINAURAL_SPLIT_PCM to output from BINAURAL, the head trajectory must + # be static. + head_traj = Path(SCRIPTS_DIR / "trajectories/const000.csv") + options.extend(["-T", str(head_traj)]) + + wav_out = ( + tmp_dir + / f"{in_format}-{ivas_bitrate}-{out_format}-dly{delay_profile_id}.wav" + ).absolute() + + trace_out = wav_out.with_suffix(".trace") + options.extend(["-Tracefile", str(trace_out), "-no_delay_cmp"]) + + if out_format == "BINAURAL_SPLIT_PCM": + isar_md_file = wav_out.with_suffix(".isarmd") + options.extend(["-om", str(isar_md_file)]) + else: + isar_md_file = None + + dut_decoder_frontend.run( + out_format, + sampling_rate_khz, + bitstream_file, + wav_out, + netsim_profile=delay_profile_path, + add_option_list=options, + ) + + return wav_out, trace_out, isar_md_file + + wav_out_bin, trace_out_bin, _ = run_decoder("BINAURAL") + wav_out_sr, trace_out_sr, _ = run_decoder("BINAURAL_SPLIT_PCM") + + # Note regarding delay alignment: both output audio files contain the same decoder delay. + # + # - When outputting to BINAURAL with -no_delay_cmp, decoder delay is present in the audio + # output, as expected. + # + # - When outputting to BINAURAL_SPLIT_PCM, decoder delay is never compensated in output + # audio (irrespective of the -no_delay_cmp flag). The delay value is saved in the ISAR + # metadata file and compensated at the post-rendering stage. + audio_sr, _ = audiofile.readfile(str(wav_out_sr)) + audio_bin, _ = audiofile.readfile(str(wav_out_bin)) + + # Ensure audio and tracefiles are BE + audio_cmp_result = audioarray.compare( + audio_bin, audio_sr, fs=sampling_rate_khz * 1000, per_frame=False + ) + tracefiles_equal = filecmp.cmp(trace_out_bin, trace_out_sr) + failed = not audio_cmp_result["bitexact"] or not tracefiles_equal + if failed: + message = [] + if not audio_cmp_result["bitexact"]: + message.append( + "Difference found between delay-aligned BINAURAL audio and BINAURAL_SPLIT_PCM audio! " + f"Max abs diff: {audio_cmp_result['max_abs_diff']}" + ) + if not tracefiles_equal: + message.append( + "Difference found between BINAURAL tracefile and BINAURAL_SPLIT_PCM tracefile!" + ) + pytest.fail("; ".join(message)) diff --git a/tests/split_rendering/utils.py b/tests/split_rendering/utils.py index 9e80cee070..39fe5cf1a7 100644 --- a/tests/split_rendering/utils.py +++ b/tests/split_rendering/utils.py @@ -30,31 +30,38 @@ accordance with the laws of the Federal Republic of Germany excluding its confli the United Nations Convention on Contracts on the International Sales of Goods. """ -import sys import re +import sys from pathlib import Path from tempfile import TemporaryDirectory from typing import Tuple, Optional +from os import path import numpy as np import pytest import logging +from tests.renderer.constants import ( + BIN_SUFFIX_MERGETARGET, + FORMAT_TO_FILE_COMPARETEST, + FORMAT_TO_FILE_SMOKETEST, + FORMAT_TO_METADATA_FILES, +) from tests.renderer.utils import ( - check_BE, - run_ivas_isar_enc_cmd, - run_ivas_isar_dec_cmd, - run_isar_post_rend_cmd, run_isar_ext_rend_cmd, + run_isar_post_rend_cmd, + run_ivas_isar_dec_cmd, + run_ivas_isar_enc_cmd, + run_cmd, ) from tests.split_rendering.constants import * + from ..cmp_pcm import cmp_pcm 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 @@ -111,7 +118,7 @@ def check_xfail( f"Unsupported configuration with {in_fmt} at IVAS bitrate {ivas_bitrate}bps" ) - if not "0dof" in render_config and pre_rend_fr != 20: + if "0dof" not in render_config and pre_rend_fr != 20: pytest.xfail("pose correction (== !0dof) is expected to use 20ms") if ( @@ -140,27 +147,6 @@ def check_xfail( "unsupported framing: LCLD codec doesn't support aggregation. Pre-renderer (ISAR) frame size must match LCLD frame size." ) - -def truncate_signal( - in_file: Path, - out_file: Path, -) -> None: - """ - Truncate the signal in in_file to maximum INPUT_DURATION_SEC seconds, - and write the truncated signal to out_file - """ - data, fs = readfile(in_file) - - if data.ndim == 1: - data_out = data[: INPUT_DURATION_SEC * fs] - elif data.ndim == 2: - data_out = data[: INPUT_DURATION_SEC * fs, :] - else: - raise ValueError(f"Cannot truncate data with dimension of {data.ndim}") - - writefile(out_file, data_out, fs) - - def run_full_chain_split_rendering( record_property, props_to_record, @@ -192,11 +178,13 @@ def run_full_chain_split_rendering( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - cut_in_file = tmp_dir.joinpath("cut_input.wav") renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") filename_base = f"{in_fmt}_{bitrate}_{renderer_fmt_for_filename}_full_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}" + if delay_profile: + filename_base += f"_{delay_profile.stem}" + ivas_bitstream_stem = f"{filename_base}.192" # NOTE: the split bitstream files need to have ".bit" extension otherwise the conformance test breaks split_bitstream_stem = f"{filename_base}.splt.bit" @@ -213,18 +201,15 @@ def run_full_chain_split_rendering( ivas_bitstream = output_path_base.joinpath(ivas_bitstream_stem) split_bitstream = output_path_base.joinpath(split_bitstream_stem) out_file = output_path_base.joinpath(out_file_stem) + if renderer_fmt == "BINAURAL_SPLIT_PCM": split_md_file = output_path_base.joinpath(split_md_file_stem) # check for metadata files - if in_fmt.upper().startswith("OSBA"): + if in_fmt.upper().startswith("ISM") and "SBA" in in_fmt.upper(): # use same MD as ISM - in_meta_files = FORMAT_TO_METADATA_FILES[f"ISM{in_fmt[5]}"] - elif ( - in_fmt.upper().startswith("ISM") - or in_fmt.upper().startswith("MASA") - or in_fmt.upper().startswith("OMASA") - ): + in_meta_files = FORMAT_TO_METADATA_FILES[f"ISM{in_fmt[3]}"] + elif in_fmt.upper().startswith("ISM") or in_fmt.upper().startswith("MASA"): in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] else: in_meta_files = None @@ -239,8 +224,7 @@ def run_full_chain_split_rendering( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: in_file = FORMAT_TO_FILE_COMPARETEST[in_fmt] - truncate_signal(in_file, cut_in_file) - + cut_in_file = in_file.with_stem(in_file.stem + "_cut") enc_cmd[3] = str(cut_in_file) else: enc_cmd[3] = str(FORMAT_TO_FILE_SMOKETEST[in_fmt]) @@ -251,7 +235,7 @@ def run_full_chain_split_rendering( enc_cmd[1:1] = FORMAT_TO_IVAS_COD_FORMAT[in_fmt] - run_ivas_isar_enc_cmd(enc_cmd) + run_ivas_isar_enc_cmd(enc_cmd, test_info=test_info) if delay_profile: rtp_bitstream = ivas_bitstream.with_suffix(".netsimout") @@ -286,8 +270,8 @@ def run_full_chain_split_rendering( if delay_profile: dec_cmd[5:5] = ["-voip"] - run_ivas_isar_dec_cmd(dec_cmd) - + run_ivas_isar_dec_cmd(dec_cmd, test_info=test_info) + # run split renderer post_rend_cmd = SPLIT_POST_REND_CMD[:] @@ -303,49 +287,31 @@ def run_full_chain_split_rendering( if renderer_fmt == "BINAURAL_SPLIT_PCM": post_rend_cmd[7:7] = ["-im", str(split_md_file)] - run_isar_post_rend_cmd(post_rend_cmd) + run_isar_post_rend_cmd(post_rend_cmd, test_info=test_info) if test_info.config.option.create_cut: # CUT creation mode will run a comparison with REF out_file_ref = str(OUTPUT_PATH_REF.joinpath(out_file_stem)) - try: - ref, ref_fs = readfile(out_file_ref) - except FileNotFoundError: - pytest.fail( - f"Reference vector not found! Ensure they were created with the --create_ref argument.\n{out_file_ref}" - ) - - cut, cut_fs = readfile(out_file) - - if not get_mld: - [diff_found, snr, gain_b, max_diff] = check_BE( - test_info, ref, ref_fs, cut, cut_fs - ) - if diff_found: - pytest.fail( - f"CuT not BE to REF! SNR : {snr:3.2f} dB, Gain CuT: {gain_b:1.3f}, Max Diff = {int(max_diff)}" - ) - else: - output_differs, reason = cmp_pcm( - out_file, - out_file_ref, - 2, # is always "BINAURAL" - ref_fs, - get_mld=get_mld, - mld_lim=mld_lim, - get_ssnr=get_ssnr, - get_odg=get_odg, - get_odg_bin=get_odg_bin, - ) - - # NOTE: split comparison not implemented for split rendering, always unpack values - reason = reason[0] - output_differs = output_differs[0] - - props = parse_properties(reason, output_differs, props_to_record) - for k, v in props.items(): - record_property(k, v) + output_differs, reason = cmp_pcm( + out_file, + out_file_ref, + 2, # is always "BINAURAL" + 48000, # currently only 48 kHz tests + get_mld=get_mld, + mld_lim=mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + ) + + # NOTE: split comparison not implemented for split rendering, always unpack values + reason = reason[0] + output_differs = output_differs[0] + + props = parse_properties(reason, output_differs, props_to_record) + for k, v in props.items(): + record_property(k, v) if output_differs: logging.error(f"Encoder command line was: {' '.join(enc_cmd)}") @@ -386,33 +352,39 @@ def run_external_split_rendering( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - cut_in_file = tmp_dir.joinpath("cut_input.wav") - split_bitstream = tmp_dir.joinpath("split.bit") + + 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}" + + if plc_error_pattern: + filename_base += f"_plc_{plc_error_pattern.stem}" + + split_bitstream_stem = f"{filename_base}.splt.bit" if renderer_fmt == "BINAURAL_SPLIT_PCM": - split_md_file = tmp_dir.joinpath("split_md.bin") + split_md_file_stem = f"{filename_base}.spltmd.bit" - renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") - out_file_stem = f"{in_fmt}_{renderer_fmt_for_filename}_ext_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}.wav" + out_file_stem = f"{filename_base}.wav" if test_info.config.option.create_ref: output_path_base = OUTPUT_PATH_REF else: output_path_base = OUTPUT_PATH_CUT + split_bitstream = output_path_base.joinpath(split_bitstream_stem) out_file = output_path_base.joinpath(out_file_stem) - - if plc_error_pattern: - out_file = out_file.with_stem( - f"{out_file.stem}_plc_{plc_error_pattern.stem}" - ) + if renderer_fmt == "BINAURAL_SPLIT_PCM": + split_md_file = output_path_base.joinpath(split_md_file_stem) # check for metadata files - if in_fmt.upper().startswith("ISM") or in_fmt.upper().startswith("MASA"): + if in_fmt.upper().startswith("ISM") and "SBA" in in_fmt.upper(): + # use name MD as ISM + in_meta_files = FORMAT_TO_METADATA_FILES[f"ISM{in_fmt[3]}"] + elif in_fmt.upper().startswith("ISM") or in_fmt.upper().startswith("MASA"): in_meta_files = FORMAT_TO_METADATA_FILES[in_fmt] else: in_meta_files = None - # generate split-rendering bitstream + # run ISAR pre-renderer split_pre_cmd = SPLIT_PRE_REND_CMD[:] if test_info.config.option.create_ref: @@ -422,8 +394,7 @@ def run_external_split_rendering( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: in_file = FORMAT_TO_FILE_COMPARETEST[in_fmt] - truncate_signal(in_file, cut_in_file) - + cut_in_file = in_file.with_stem(in_file.stem + "_cut") split_pre_cmd[6] = str(cut_in_file) else: split_pre_cmd[6] = str(FORMAT_TO_FILE_SMOKETEST[in_fmt]) @@ -439,9 +410,9 @@ def run_external_split_rendering( if in_meta_files: split_pre_cmd[9:9] = ["-im", *in_meta_files] - run_isar_ext_rend_cmd(split_pre_cmd) + run_isar_ext_rend_cmd(split_pre_cmd, test_info=test_info) - # run split renderer + # run ISAR post-renderer split_post_cmd = SPLIT_POST_REND_CMD[:] if test_info.config.option.create_ref: @@ -459,53 +430,30 @@ def run_external_split_rendering( if plc_error_pattern: split_post_cmd[1:1] = ["-prbfi", str(plc_error_pattern)] - run_isar_ext_rend_cmd(split_post_cmd) + run_isar_ext_rend_cmd(split_post_cmd, test_info=test_info) if test_info.config.option.create_cut: # CUT creation mode will run a comparison with REF out_file_ref = OUTPUT_PATH_REF.joinpath(out_file_stem) - if plc_error_pattern: - out_file_ref = out_file_ref.with_stem( - f"{out_file_ref.stem}_plc_{plc_error_pattern.stem}" - ) - - try: - ref, ref_fs = readfile(out_file_ref) - except FileNotFoundError: - pytest.fail( - f"Reference vector not found! Ensure they were created with the --create_ref argument.\n{out_file_ref}" - ) - - cut, cut_fs = readfile(out_file) - - if not get_mld: - [diff_found, snr, gain_b, max_diff] = check_BE( - test_info, ref, ref_fs, cut, cut_fs - ) - if diff_found: - pytest.fail( - f"CuT not BE to REF! SNR : {snr:3.2f} dB, Gain CuT: {gain_b:1.3f}, Max Diff = {int(max_diff)}" - ) - else: - # see constants.py - output_differs, reason = cmp_pcm( - out_file, - out_file_ref, - 2, # is always "BINAURAL", - ref_fs, - get_mld=get_mld, - mld_lim=mld_lim, - get_ssnr=get_ssnr, - get_odg=get_odg, - get_odg_bin=get_odg_bin, - ) - # NOTE: split comparison not implemented for split rendering, always unpack values - reason = reason[0] - output_differs = output_differs[0] - - props = parse_properties(reason, output_differs, props_to_record) - for k, v in props.items(): - record_property(k, v) + + output_differs, reason = cmp_pcm( + out_file, + out_file_ref, + 2, # is always "BINAURAL", + 48000, # currently only 48 kHz tests + get_mld=get_mld, + mld_lim=mld_lim, + get_ssnr=get_ssnr, + get_odg=get_odg, + get_odg_bin=get_odg_bin, + ) + # NOTE: split comparison not implemented for split rendering, always unpack values + reason = reason[0] + output_differs = output_differs[0] + + props = parse_properties(reason, output_differs, props_to_record) + for k, v in props.items(): + record_property(k, v) if output_differs: logging.error(f"Split Pre command line was: {' '.join(split_pre_cmd)}") diff --git a/tests/test_be_for_jbm_neutral_dly_profile.py b/tests/test_be_for_jbm_neutral_dly_profile.py index ce81d091c7..dee54b7d94 100644 --- a/tests/test_be_for_jbm_neutral_dly_profile.py +++ b/tests/test_be_for_jbm_neutral_dly_profile.py @@ -21,7 +21,7 @@ DTX_OFF = "DTX_OFF" # testcases where DTX is supported TESTCASES_WITH_DTX = [ # stereo - ["stereo", 32000, "STEREO"], + ["stereo", 32000, "EXT"], ["stereo", 48000, "MONO"], ["stereo", 16400, "5_1"], ["stereo", 256000, "7_1_4"], @@ -43,9 +43,8 @@ TESTCASES_WITH_DTX = [ ["MASA1TC", 128000, "EXT"], # SBA ["HOA3", 64000, "BINAURAL"], - ["HOA2", 80000, "HOA2"], - # NOTE: commented because still some bug in BASOP float reference - # ["FOA", 13200, "stereo"], + ["HOA2", 80000, "EXT"], + ["FOA", 13200, "stereo"], ] # testcases with no DTX support @@ -57,30 +56,27 @@ TESTCASES_NO_DTX = [ # McMasa ["MC_5_1", 16400, "BINAURAL_ROOM_IR"], ["MC_7_1_4", 80000, "mono"], - ["MC_5_1_2", 24400, "5_1_2"], + ["MC_5_1_2", 24400, "EXT"], # paramMC ["MC_5_1_2", 48000, "BINAURAL"], - ["MC_7_1", 80000, "7_1"], + ["MC_7_1", 80000, "EXT"], ["MC_7_1_4", 128000, "FOA"], # paramUpmix ["MC_7_1_4", 160000, "stereo"], # discrete MC ["MC_5_1_2", 512000, "BINAURAL_ROOM_REVERB"], - ["MC_7_1", 128000, "7_1"], + ["MC_7_1", 128000, "EXT"], ["MC_7_1_4", 256000, "5_1"], # OMASA ["OMASA_ISM1", 512000, "BINAURAL"], ["OMASA_ISM2", 24400, "MONO"], ["OMASA_ISM3", 80000, "7_1_4"], ["OMASA_ISM4", 64000, "HOA3"], - # NOTE: commented because EXT unsupported currently - # ["OMASA_ISM2", 32000, "EXT"], + ["OMASA_ISM2", 32000, "EXT"], # OSBA ["OSBA_ISM2_HOA2", 64000, "BINAURAL_ROOM_IR"], - # NOTE: commented because still some bug in BASOP float reference - # ["OSBA_ISM4_FOA", 512000, "BINAURAL_ROOM_REVERB"], - # NOTE: commented because EXT unsupported currently - # ["OSBA_ISM3_HOA3", 128000, "EXT"], + ["OSBA_ISM4_FOA", 512000, "BINAURAL_ROOM_REVERB"], + ["OSBA_ISM3_HOA3", 128000, "EXT"], ["OSBA_ISM2_HOA3", 96000, "5_1"], ["OSBA_ISM1_HOA2", 32000, "mono"], # BINAURAL_SPLIT_PCM as output @@ -155,12 +151,7 @@ def get_options_dec( options = [] if "BINAURAL_SPLIT" in output_format.upper(): - options.extend( - [ - "-render_config", - str(RENDER_CFG_DIR / "split_renderer_config_3dof_512k_default.txt"), - ] - ) + options.extend(["-render_config", str(RENDER_CFG_DIR / "split_renderer_config_3dof_512k_default.txt")]) if output_format.upper() == "BINAURAL_SPLIT_PCM": options.extend(["-om", str(output_file.with_suffix(".isarmd"))]) @@ -215,7 +206,6 @@ OUTPUT_FOLDER_IF_KEEP_FILES_NEUTRAL = OUTPUT_FOLDER_IF_KEEP_FILES.joinpath( "neutral-profile" ) - def compare_audio(non_voip_output, voip_output, sampling_rate_khz): # compare no-jbm and jbm output x, _ = audiofile.readfile(non_voip_output) @@ -243,7 +233,6 @@ def compare_isar_files(non_voip_isar, voip_isar): "Difference between no jbm and zero-delay jbm decoding found! ISAR files differ" ) - @pytest.mark.parametrize( "in_format,bitrate,out_format", TESTCASES_NO_DTX + TESTCASES_WITH_DTX ) @@ -256,9 +245,6 @@ def test_be_for_jbm_neutral_dly_profile_no_dtx( dut_postrend_frontend, keep_files, ): - if out_format == "BINAURAL_SPLIT_CODED" or out_format == "BINAURAL_SPLIT_PCM": - pytest.skip(f"{out_format} skipped in BASOP until suported properly") - run_test( in_format, bitrate, @@ -271,9 +257,6 @@ def test_be_for_jbm_neutral_dly_profile_no_dtx( ) -@pytest.mark.skip( - reason="non-BE bug for DTX cases not yet fixed in BASOP ivas-float-update" -) @pytest.mark.parametrize("in_format,bitrate,out_format", TESTCASES_WITH_DTX) def test_be_for_jbm_neutral_dly_profile_with_dtx( in_format, @@ -364,9 +347,7 @@ def run_test( ) # run decoder with network simulation - output_jbm = output_dir_neutral.joinpath( - output.with_suffix(f".jbm-0.{output_ext}").name - ) + output_jbm = output_dir_neutral.joinpath(output.with_suffix(f".jbm-0.{output_ext}").name) voip_options = get_options_dec(out_format, output_jbm, is_voip=True) dut_decoder_frontend.run( out_format, @@ -398,9 +379,7 @@ def run_test( return # Render non-voip output - postrend_output = output_dir_no_jbm.joinpath( - output.with_suffix(".postrend.wav").name - ) + postrend_output = output_dir_no_jbm.joinpath(output.with_suffix(".postrend.wav").name) dut_postrend_frontend.run( sampling_rate_khz, output, @@ -410,9 +389,7 @@ def run_test( ) # Render voip output - postrend_output_voip = output_dir_neutral.joinpath( - output_jbm.with_suffix(".postrend.wav").name - ) + postrend_output_voip = output_dir_neutral.joinpath(output_jbm.with_suffix(".postrend.wav").name) dut_postrend_frontend.run( sampling_rate_khz, output_jbm, diff --git a/tests/test_enc_passthrough.py b/tests/test_enc_passthrough.py index a2e7458922..83a3f51989 100644 --- a/tests/test_enc_passthrough.py +++ b/tests/test_enc_passthrough.py @@ -195,6 +195,7 @@ def test_enc( get_odg_bin, compare_to_input, compare_enc_dmx, + split_comparison, ): enc_opts, dec_opts, sim_opts, eid_opts = test_dict[test_tag] @@ -228,6 +229,7 @@ def test_enc( get_odg_bin, compare_to_input, compare_enc_dmx, + split_comparison, ) \ No newline at end of file -- GitLab From b0bba5241227d9e316b8f2c543f41232672c397c Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 18 Nov 2025 16:12:13 +0100 Subject: [PATCH 05/40] synch evs prm file from main --- scripts/config/self_test_evs.prm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/config/self_test_evs.prm b/scripts/config/self_test_evs.prm index ec611fee2a..b68b9509a3 100644 --- a/scripts/config/self_test_evs.prm +++ b/scripts/config/self_test_evs.prm @@ -250,14 +250,14 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 // Codec A at 13.20 kbps, 32kHz in, 32kHz out, DTX, JBM Prof 5 ../IVAS_cod -dtx 13200 32 testv/stv32c.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 -../IVAS_dec -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_13k20_32-32_DTX_JBM5.tst +../IVAS_dec -no_delay_cmp -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_13k20_32-32_DTX_JBM5.tst // Codec B at 16.40 kbps, 32kHz in, 32kHz out, DTX, JBM Prof 5 ../IVAS_cod -dtx 16400 32 testv/stv32c.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 -../IVAS_dec -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_16k40_32-32_DTX_JBM5.tst +../IVAS_dec -no_delay_cmp -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_16k40_32-32_DTX_JBM5.tst // Codec B at 13.20 kbps, 32kHz in, 32kHz out, JBM Prof 9, Channel aware ../IVAS_cod -rf 13200 32 testv/stv32c.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 -../IVAS_dec -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_13k20_CA_32-32_JBM9.tst +../IVAS_dec -no_delay_cmp -Tracefile tracefile_dec -VOIP 32 netsimoutput testv/stv32c_13k20_CA_32-32_JBM9.tst -- GitLab From 30ac884b8244e161cc634d57a46134788e02e231 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 18 Nov 2025 16:38:33 +0100 Subject: [PATCH 06/40] synch gitlab-ci.yml --- .gitlab-ci.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a34939ac42..1a1058c34d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -435,7 +435,7 @@ codec-smoke-test: - if cat smoke_test_output_hrtf.txt | grep -c "failed"; then echo "Smoke test with external hrtf files failed"; ret_val=1; fi - exit $ret_val artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--stage-$CI_JOB_STAGE--results" + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" expire_in: 1 week when: always paths: @@ -523,7 +523,7 @@ pytest-compare-20ms-and-5ms-rendering: - if [ $exit_code10 -ne 0 ]; then echo "Non-bitexact cases encountered with 10ms rendering!"; exit_code=1; fi - if [ $exit_code -ne 0 ]; then exit $EXIT_CODE_FAIL; fi artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--stage-$CI_JOB_STAGE--results" + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" expire_in: 1 week when: always expose_as: "pytest 5ms and 10ms vs 20ms results" @@ -824,6 +824,12 @@ ivas-pytest-on-merge-request: stage: compare needs: ["build-codec-linux-cmake", "codec-smoke-test"] timeout: "14 minutes" + parallel: + matrix: + # note: keep in sync with list in $TESTS_DIR_CODEC_BE_ON_MR + - PYTEST_SCRIPT: + - test_param_file.py + - test_sba.py script: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - commits_behind_count="$(bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/get-commits-behind-count.sh)" @@ -841,7 +847,7 @@ ivas-pytest-on-merge-request: ### prepare pytest # create references - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --update_ref 1 + - python3 -m pytest "$TESTS_DIR_CODEC_BE_ON_MR"/"$PYTEST_SCRIPT" -v --update_ref 1 ### Run test using branch scripts and input - if [ $ref_using_main == 1 ]; then git checkout $source_branch_commit_sha; fi @@ -849,7 +855,7 @@ ivas-pytest-on-merge-request: ### run pytest - exit_code=0 - testcase_timeout=60 - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --html=report.html --self-contained-html --junit-xml=report-junit.xml --testcase_timeout=$testcase_timeout || exit_code=$? + - python3 -m pytest "$TESTS_DIR_CODEC_BE_ON_MR"/"$PYTEST_SCRIPT" -v --html=report.html --self-contained-html --junit-xml=report-junit.xml --testcase_timeout=$testcase_timeout || exit_code=$? - zero_errors=$(cat report-junit.xml | grep -c 'errors="0"') || true - *merge-request-comparison-check @@ -858,7 +864,7 @@ ivas-pytest-on-merge-request: exit_codes: - 123 artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--stage-$CI_JOB_STAGE--results" + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" expire_in: 1 week when: always paths: @@ -908,7 +914,7 @@ ivas-interop-on-merge-request: exit_codes: - 123 artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--stage-$CI_JOB_STAGE--results" + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" expire_in: 1 week when: always paths: @@ -960,7 +966,7 @@ evs-pytest-on-merge-request: exit_codes: - 123 artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--stage-$CI_JOB_STAGE--results" + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" expire_in: 1 week when: always paths: @@ -1056,7 +1062,7 @@ check-first-frame-is-sid: expose_as: "logs-sidstart" expire_in: "5 days" -.lc3plus-ensure-no-code-changes: +lc3plus-ensure-no-code-changes: extends: - .test-job-linux - .rules-merge-request-to-main @@ -1068,7 +1074,7 @@ check-first-frame-is-sid: - ./scripts/lc3plus_lib_setup/get_lc3plus.sh # Ensure git reports no changes - - modified_files=$(git status -s) + - modified_files=$(git status -su lib_lc3plus) - if [[ $modified_files ]]; then printf 'LC3plus codebase was modified!\n\n'"$modified_files"'\n\n'; exit $EXIT_CODE_FAIL; fi check-bitexactness-hrtf-rom-and-file: @@ -1171,7 +1177,6 @@ be-2-evs-windows: # - cd evs_be_test # - python3 ../ci/run_evs_be_test.py - # TODO: do we still need this? # codec-comparison-on-main-push: # extends: -- GitLab From a7f5c9b28f97085e7187961989d3a91c9c87cead Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Tue, 18 Nov 2025 18:26:13 +0100 Subject: [PATCH 07/40] replace tabs by space --- readme.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index 3147d457eb..129393976b 100644 --- a/readme.txt +++ b/readme.txt @@ -35,9 +35,9 @@ These files represent the 3GPP EVS Codec Extension for Immersive Voice and Audio Services (IVAS) floating-point C simulation. All code is writtten in ISO/IEC C99. The system is implemented as six separate programs: - IVAS_cod IVAS Encoder - IVAS_dec IVAS Decoder - IVAS_rend IVAS External Renderer + IVAS_cod IVAS Encoder + IVAS_dec IVAS Decoder + IVAS_rend IVAS External Renderer ISAR_post_rend ISAR Post Renderer IVAS_cod_fmtsw IVAS Encoder with support for format switching ambi_converter example program for Ambisonics format conversion -- GitLab From 3ddc5e426295bc5066600a0a747c2cae1c96a278 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Tue, 18 Nov 2025 21:26:09 +0100 Subject: [PATCH 08/40] update version number printout --- lib_com/disclaimer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_com/disclaimer.c b/lib_com/disclaimer.c index 170d58a3fa..736a212235 100644 --- a/lib_com/disclaimer.c +++ b/lib_com/disclaimer.c @@ -47,7 +47,7 @@ int16_t print_disclaimer( FILE *fPtr ) { fprintf( fPtr, "\n==================================================================================================\n" ); - fprintf( fPtr, " \n IVAS Codec Version IVAS-FL-2.0\n" ); + fprintf( fPtr, " \n 3GPP TS26.258 IVAS Codec Version IVAS-FL-3.0 (floating-point C-Code)\n" ); fprintf( fPtr, " \n" ); fprintf( fPtr, " Based on EVS Codec (Floating Point) 3GPP TS26.443 Nov 04, 2021,\n" ); fprintf( fPtr, " Version 12.14.0 / 13.10.0 / 14.6.0 / 15.4.0 / 16.3.0\n" ); -- GitLab From a5382b3738896d047b138e86c34b7e8f91416fd8 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Tue, 18 Nov 2025 23:53:24 +0100 Subject: [PATCH 09/40] harmonize whitespace with FX --- lib_com/disclaimer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib_com/disclaimer.c b/lib_com/disclaimer.c index 736a212235..ac95ffb59c 100644 --- a/lib_com/disclaimer.c +++ b/lib_com/disclaimer.c @@ -47,10 +47,12 @@ int16_t print_disclaimer( FILE *fPtr ) { fprintf( fPtr, "\n==================================================================================================\n" ); - fprintf( fPtr, " \n 3GPP TS26.258 IVAS Codec Version IVAS-FL-3.0 (floating-point C-Code)\n" ); - fprintf( fPtr, " \n" ); + fprintf( fPtr, "\n" ); + fprintf( fPtr, " 3GPP TS26.258 IVAS Codec Version IVAS-FL-3.0 (floating-point C-Code)\n" ); + fprintf( fPtr, "\n" ); fprintf( fPtr, " Based on EVS Codec (Floating Point) 3GPP TS26.443 Nov 04, 2021,\n" ); fprintf( fPtr, " Version 12.14.0 / 13.10.0 / 14.6.0 / 15.4.0 / 16.3.0\n" ); + fprintf( fPtr, "\n" ); fprintf( fPtr, "==================================================================================================\n\n" ); return 0; -- GitLab From 8b673923b048dcff032fdc04434ff5c2a2187dc4 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Tue, 18 Nov 2025 23:55:38 +0100 Subject: [PATCH 10/40] fix patch script --- scripts/patch_code_headers.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/patch_code_headers.sh b/scripts/patch_code_headers.sh index d65dcb6f65..3140b0575a 100755 --- a/scripts/patch_code_headers.sh +++ b/scripts/patch_code_headers.sh @@ -58,7 +58,7 @@ fi #date="Nov 11, 2025" date=`date "+%b %d, %Y"` -version="IVAS-FL-3.0-RC01" +version="IVAS-FL-3.0" ########################## # # @@ -72,7 +72,7 @@ if [ -d ${WORKDIR}/lib_basop ]; then fi if [ $BASOP -eq 1 ]; then - version="IVAS-FX-3.0-RC01" + version="IVAS-FX-3.0" fi # @@ -178,7 +178,7 @@ rm -f $tmpfile # Patch Printout # -sed -i.bak -e "s/IVAS\ Codec\ Baseline/IVAS\ Codec\ Version\ $version/g" $WORKDIR/lib_com/disclaimer.c +#sed -i.bak -e "s/IVAS\ Codec\ Baseline/IVAS\ Codec\ Version\ $version/g" $WORKDIR/lib_com/disclaimer.c # # Patch Matlab Scripts -- GitLab From c1dc8a1cf7aec74944ddadaa141bb0ff54d47940 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 18 Nov 2025 22:59:34 -0600 Subject: [PATCH 11/40] Implement changes according to new spec - 5 bits for PI size - Only 1 byte per orientation component - Add compact positions for ISM and reverse ISM PI --- lib_com/options.h | 1 + lib_util/ivas_rtp_file.c | 64 +++++- lib_util/ivas_rtp_internal.h | 11 ++ lib_util/ivas_rtp_payload.c | 12 ++ lib_util/ivas_rtp_pi_data.c | 367 +++++++++++++++++++++++++++++++++++ lib_util/ivas_rtp_pi_data.h | 12 ++ tests/rtp/ivasrtp.py | 81 ++++++-- 7 files changed, 523 insertions(+), 25 deletions(-) diff --git a/lib_com/options.h b/lib_com/options.h index 7aa9f1c915..2d39346da6 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -169,6 +169,7 @@ #define DECODER_FORMAT_SWITCHING /* Re-initialize the decoder when the format/subformat of the incoming stream is changed */ #define RTP_SR_CODEC_FRAME_SIZE_IN_TOC_BYTE /* CR for split rendering codec framesize signalling in Toc Byte*/ #define FIX_SPLIT_RENDERING_ON_DECODER_RESTART /* Re-configure split rendering on decoder restart */ +#define RTP_UPDATES_SA4_134 /* Updates to RTP during SA4 134 */ /* ################### Start BE switches ################################# */ /* only BE switches wrt selection floating point code */ diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c index b8e79fad53..009a158c49 100644 --- a/lib_util/ivas_rtp_file.c +++ b/lib_util/ivas_rtp_file.c @@ -144,11 +144,10 @@ static ivas_error IvasRtpFile_Read( static const char *const PiDataNames[IVAS_PI_MAX_ID] = { "SCENE_ORIENTATION", "DEVICE_ORIENTATION_COMPENSATED", "DEVICE_ORIENTATION_UNCOMPENSATED", "ACOUSTIC_ENVIRONMENT", "AUDIO_DESCRIPTION", "ISM_NUM", "ISM_ID", "ISM_GAIN", "ISM_ORIENTATION", - "ISM_POSITION", "ISM_DISTANCE_ATTENUATION", "ISM_DIRECTIVITY", "DIEGETIC_TYPE", "DYNAMIC_AUDIO_SUPPRESSION_INDICATION", - "AUDIO_FOCUS_INDICATION", "RESERVED15", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION", + "ISM_POSITION", "ISM_POSITION_COMPACT", "ISM_DISTANCE_ATTENUATION", "ISM_DIRECTIVITY", "DIEGETIC_TYPE", "DYNAMIC_AUDIO_SUPPRESSION_INDICATION", + "AUDIO_FOCUS_INDICATION", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION", "DYNAMIC_AUDIO_SUPPRESSION_REQUEST", "AUDIO_FOCUS_REQUEST", "PI_LATENCY", "R_ISM_ID", "R_ISM_GAIN", - "R_ISM_ORIENTATION", "R_ISM_POSITION", "R_ISM_DIRECTION", "RESERVED27", "RESERVED28", "RESERVED29", - "RESERVED30", "NO_DATA" + "R_ISM_ORIENTATION", "R_ISM_POSITION", "R_ISM_POSITION_COMPACT", "R_ISM_DIRECTION", "RESERVED27", "RESERVED28", "RESERVED29", "NO_DATA" }; void IVAS_RTP_LogPiData( @@ -335,11 +334,17 @@ void IVAS_RTP_LogPiData( fprintf( f_piDataOut, "\n\t\t}" ); } break; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + case IVAS_PI_RESERVED27: + case IVAS_PI_RESERVED28: + case IVAS_PI_RESERVED29: +#else case IVAS_PI_RESERVED15: case IVAS_PI_RESERVED27: case IVAS_PI_RESERVED28: case IVAS_PI_RESERVED29: case IVAS_PI_RESERVED30: +#endif { fprintf( f_piDataOut, "{}" ); } @@ -413,6 +418,22 @@ void IVAS_RTP_LogPiData( fprintf( f_piDataOut, "\n\t\t]" ); } break; +#ifdef RTP_UPDATES_SA4_134 + case IVAS_PI_ISM_POSITION_COMPACT: + { + fprintf( f_piDataOut, "[\n" ); + for ( n = 0; n < cur->data.ismPositionCompact.numObjects; n++ ) + { + if ( n != 0 ) + { + fprintf( f_piDataOut, ",\n" ); + } + fprintf( f_piDataOut, "\t\t\t{\n\t\t\t\t\"x\": %f,\n\t\t\t\t\"y\": %f,\n\t\t\t\t\"z\": %f \n\t\t\t}", cur->data.ismPositionCompact.position[n].x, cur->data.ismPositionCompact.position[n].y, cur->data.ismPositionCompact.position[n].z ); + } + fprintf( f_piDataOut, "\n\t\t]" ); + } + break; +#endif case IVAS_PI_ISM_DISTANCE_ATTENUATION: { fprintf( f_piDataOut, "[\n" ); @@ -491,6 +512,14 @@ void IVAS_RTP_LogPiData( cur->data.ismEditPosition.position.x, cur->data.ismEditPosition.position.y, cur->data.ismEditPosition.position.z ); } break; +#ifdef RTP_UPDATES_SA4_134 + case IVAS_PI_R_ISM_POSITION_COMPACT: + { + fprintf( f_piDataOut, "{\n\t\t\t\"x\": %f,\n\t\t\t\"y\": %f,\n\t\t\t\"z\": %f \n\t\t}", + cur->data.ismEditPositionCompact.position.x, cur->data.ismEditPositionCompact.position.y, cur->data.ismEditPositionCompact.position.z ); + } + break; +#endif #endif case IVAS_PI_R_ISM_DIRECTION: #ifdef REVERSE_ISM_PI_DATA @@ -631,6 +660,20 @@ void IVAS_RTP_WriteExtPiData( } } break; +#ifdef RTP_UPDATES_SA4_134 + case IVAS_PI_ISM_POSITION_COMPACT: + { + for ( i = 0; i < numObj; ++i ) + { + if ( i != 0 ) + { + fprintf( f_piDataOut, "," ); + } + fprintf( f_piDataOut, "%f,%f,%f", cur->data.ismPositionCompact.position[i].x, cur->data.ismPositionCompact.position[i].y, cur->data.ismPositionCompact.position[i].z ); + } + } + break; +#endif case IVAS_PI_ISM_DISTANCE_ATTENUATION: { for ( i = 0; i < numObj; ++i ) @@ -746,16 +789,29 @@ void IVAS_RTP_WriteExtPiData( fprintf( f_piDataOut, "%f,%f,%f", cur->data.ismEditPosition.position.x, cur->data.ismEditPosition.position.y, cur->data.ismEditPosition.position.z ); } break; +#ifdef RTP_UPDATES_SA4_134 + case IVAS_PI_R_ISM_POSITION_COMPACT: + { + fprintf( f_piDataOut, "%f,%f,%f", cur->data.ismEditPositionCompact.position.x, cur->data.ismEditPositionCompact.position.y, cur->data.ismEditPositionCompact.position.z ); + } + break; +#endif case IVAS_PI_R_ISM_DIRECTION: { fprintf( f_piDataOut, "%f,%f", cur->data.ismEditDirection.azimuth, cur->data.ismEditDirection.elevation ); } break; +#ifdef RTP_UPDATES_SA4_134 + case IVAS_PI_RESERVED27: + case IVAS_PI_RESERVED28: + case IVAS_PI_RESERVED29: +#else case IVAS_PI_RESERVED15: case IVAS_PI_RESERVED27: case IVAS_PI_RESERVED28: case IVAS_PI_RESERVED29: case IVAS_PI_RESERVED30: +#endif break; #endif /* RTP_S4_251135_CR26253_0016_REV1 */ } diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index a550892548..78f416879f 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -51,6 +51,10 @@ enum MASK_BITS #ifdef REVERSE_ISM_PI_DATA MASK_9BIT = 0x1FF, #endif +#ifdef RTP_UPDATES_SA4_134 + MASK_10BIT = 0x2FF, + MASK_11BIT = 0x3FF, +#endif }; @@ -69,6 +73,13 @@ enum MASK_BITS #define MAX_PI_POSITION_METERS ( 327.68f ) #define FLOAT_FROM_Q15( q15Val ) ( (float) ( q15Val ) / 32768.0f ) +#ifdef RTP_UPDATES_SA4_134 +#define MAX_PI_COMPACT_POSITION_XY_METERS ( 10.24f ) +#define MAX_PI_COMPACT_POSITION_Z_METERS ( 5.12f ) +#define FLOAT_FROM_Q11( q11Val ) ( (float) ( q11Val ) / 2048.0f ) +#define FLOAT_FROM_Q10( q10Val ) ( (float) ( q10Val ) / 1024.0f ) +#define FLOAT_FROM_Q7( q7Val ) ( (float) ( q7Val ) / 128.0f ) +#endif extern const float mapDSR[1u << NBITS_DSR]; extern const float mapRT60[1u << NBITS_RT60]; diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 0398d9ce40..ce529094f0 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -1585,6 +1585,17 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes PM = ( piHeader0 & ( ~MASK_5BIT ) ) & MASK_7BIT; /* PI Marker Bits */ piDataType = ( piHeader0 & MASK_5BIT ); +#ifdef RTP_UPDATES_SA4_134 + do + { + byte = payload->buffer[nBytes++]; + piSize += ( byte & MASK_5BIT ); + if ( nBytes >= payload->length ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading piSize" ); + } + } while ( byte == 32 ); +#else do { byte = payload->buffer[nBytes++]; @@ -1594,6 +1605,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading piSize" ); } } while ( byte == 255 ); +#endif if ( piDataType == IVAS_PI_NO_DATA ) { diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index b9b7bcc7e4..e8a7d05304 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -79,6 +79,47 @@ static int16_t ivasPayload_convertToQ15( float value ) return (int16_t) ( value ); } +#ifdef RTP_UPDATES_SA4_134 +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ11() + * + * Convert a float value into a Q11 encoded value. + *-----------------------------------------------------------------------*/ +static int16_t ivasPayload_convertToQ11( float value ) +{ + value = ( value * 2048.0f ); + value = value > +2048.0f ? +2048.0f : value; + value = value < -2048.0f ? -2048.0f : value; + return (int16_t) ( value ); +} + +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ10() + * + * Convert a float value into a Q10 encoded value. + *-----------------------------------------------------------------------*/ +static int16_t ivasPayload_convertToQ10( float value ) +{ + value = ( value * 1024.0f ); + value = value > +1024.0f ? +1024.0f : value; + value = value < -1024.0f ? -1024.0f : value; + return (int16_t) ( value ); +} + +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ7() + * + * Convert a float value into a Q7 encoded value. + *-----------------------------------------------------------------------*/ +static int16_t ivasPayload_convertToQ7( float value ) +{ + value = ( value * 128.0f ); + value = value > +128.0f ? +128.0f : value; + value = value < -128.0f ? -128.0f : value; + return (int16_t) ( value ); +} +#endif + static ivas_error packUnsupportedData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { (void) piData; @@ -152,18 +193,34 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in Orientation PI data" ); } +#ifdef RTP_UPDATES_SA4_134 + /* Orientation data is 4 bytes, header is 2 bytes */ + if ( maxDataBytes < 4 + 2 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); + } +#else /* Orientation data is 8 bytes, header is 2 bytes */ if ( maxDataBytes < 8 + 2 ) { return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); } +#endif buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ +#ifdef RTP_UPDATES_SA4_134 + buffer[nBytes++] = 4; + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.w ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.x ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.y ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.z ); +#else buffer[nBytes++] = 8; nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.w ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.x ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.y ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.z ) ); +#endif *nBytesWritten = nBytes; return IVAS_ERR_OK; @@ -186,13 +243,31 @@ static ivas_error packISMOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t { return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in Orientation PI data" ); } +#ifdef RTP_UPDATES_SA4_134 + /* Orientation data is 4 bytes, header is 2 bytes */ + if ( maxDataBytes < 4 * IVAS_MAX_NUM_OBJECTS + 2 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); + } +#else /* Orientation data is 8 bytes, header is 2 bytes */ if ( maxDataBytes < 8 * IVAS_MAX_NUM_OBJECTS + 2 ) { return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); } +#endif buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ +#ifdef RTP_UPDATES_SA4_134 + buffer[nBytes++] = (uint8_t) orientation->numObjects * 4; + for ( n = 0; n < orientation->numObjects; n++ ) + { + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation[n].w ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation[n].x ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation[n].y ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation[n].z ); + } +#else buffer[nBytes++] = (uint8_t) orientation->numObjects * 8; for ( n = 0; n < orientation->numObjects; n++ ) { @@ -201,6 +276,7 @@ static ivas_error packISMOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].y ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].z ) ); } +#endif *nBytesWritten = nBytes; return IVAS_ERR_OK; } @@ -210,6 +286,19 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte { IVAS_PIDATA_ORIENTATION *orientation = (IVAS_PIDATA_ORIENTATION *) piData; +#ifdef RTP_UPDATES_SA4_134 + /* Orientation data is 4 bytes */ + if ( numDataBytes != 4 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); + } + + piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); + orientation->orientation.w = FLOAT_FROM_Q7( buffer[0] ); + orientation->orientation.x = FLOAT_FROM_Q7( buffer[1] ); + orientation->orientation.y = FLOAT_FROM_Q7( buffer[2] ); + orientation->orientation.z = FLOAT_FROM_Q7( buffer[3] ); +#else /* Orientation data is 8 bytes */ if ( numDataBytes != 8 ) { @@ -221,6 +310,7 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte orientation->orientation.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ); orientation->orientation.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ); orientation->orientation.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) ); +#endif return IVAS_ERR_OK; } @@ -230,6 +320,25 @@ static ivas_error unpackISMOrientation( const uint8_t *buffer, uint32_t numDataB { IVAS_PIDATA_ISM_ORIENTATION *ism_orientation = (IVAS_PIDATA_ISM_ORIENTATION *) piData; +#ifdef RTP_UPDATES_SA4_134 + /* Orientation data is 4 bytes */ + uint16_t n; + if ( numDataBytes % 4 != 0 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); + } + + ism_orientation->size = sizeof( IVAS_PIDATA_ISM_ORIENTATION ); + ism_orientation->numObjects = (uint16_t) numDataBytes / 4; + + for ( n = 0; n < ism_orientation->numObjects; n++ ) + { + ism_orientation->orientation[n].w = FLOAT_FROM_Q7( buffer[4 * n] ); + ism_orientation->orientation[n].x = FLOAT_FROM_Q7( buffer[4 * n + 1] ); + ism_orientation->orientation[n].y = FLOAT_FROM_Q7( buffer[4 * n + 2] ); + ism_orientation->orientation[n].z = FLOAT_FROM_Q7( buffer[4 * n + 3] ); + } +#else /* Orientation data is 8 bytes */ uint16_t n; if ( numDataBytes % 8 != 0 ) @@ -247,6 +356,7 @@ static ivas_error unpackISMOrientation( const uint8_t *buffer, uint32_t numDataB ism_orientation->orientation[n].y = FLOAT_FROM_Q15( readInt16( &buffer[8 * n + 4] ) ); ism_orientation->orientation[n].z = FLOAT_FROM_Q15( readInt16( &buffer[8 * n + 6] ) ); } +#endif for ( ; n < IVAS_MAX_NUM_OBJECTS; n++ ) { ism_orientation->orientation[n].w = 0.0f; @@ -614,6 +724,68 @@ static ivas_error unpackListenerPosition( const uint8_t *buffer, uint32_t numDat } #endif +#ifdef RTP_UPDATES_SA4_134 +static ivas_error packPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) +{ + uint32_t nBytes = 0; + uint16_t posX, posY, posZ = 0; + const IVAS_PIDATA_POSITION *position = (const IVAS_PIDATA_POSITION *) piData; + + *nBytesWritten = 0; + + if ( piData->size != sizeof( IVAS_PIDATA_POSITION ) ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in compact position PI data" ); + } + + if ( piData->piDataType != IVAS_PI_R_ISM_POSITION ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in compact position PI data" ); + } + + /* Compact position data is 4 bytes, header is 2 bytes */ + if ( maxDataBytes < 4 + 2 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack compact position PI data" ); + } + + buffer[nBytes++] = ( position->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ + buffer[nBytes++] = 4; + posX = (uint16_t) ivasPayload_convertToQ11( position->position.x / MAX_PI_COMPACT_POSITION_XY_METERS ); + posY = (uint16_t) ivasPayload_convertToQ11( position->position.y / MAX_PI_COMPACT_POSITION_XY_METERS ); + posZ = (uint16_t) ivasPayload_convertToQ10( position->position.z / MAX_PI_COMPACT_POSITION_Z_METERS ); + buffer[nBytes++] = (uint8_t) ( posX >> 8 ); + buffer[nBytes++] = (uint8_t) ( ( ( posX & MASK_3BIT ) << 5 ) | posY >> 6 ); + buffer[nBytes++] = (uint8_t) ( ( ( posY & MASK_6BIT ) << 2 ) | posZ >> 8 ); + buffer[nBytes++] = (uint8_t) ( posZ >> 2 ); + + *nBytesWritten = nBytes; + + return IVAS_ERR_OK; +} + +static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) +{ + IVAS_PIDATA_POSITION *position = (IVAS_PIDATA_POSITION *) piData; + uint32_t compactRead = 0; + + /* Compact position data is 4 bytes */ + if ( numDataBytes != 4 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack compact position PI data" ); + } + + position->size = sizeof( IVAS_PIDATA_POSITION ); + + compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); + position->position.x = FLOAT_FROM_Q11( compactRead >> 21 ) * MAX_PI_COMPACT_POSITION_XY_METERS; + position->position.y = FLOAT_FROM_Q11( ( compactRead >> 10 ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; + position->position.z = FLOAT_FROM_Q10( compactRead & MASK_10BIT ) * MAX_PI_COMPACT_POSITION_Z_METERS; + + return IVAS_ERR_OK; +} +#endif + #ifdef ISM_PI_DATA static ivas_error packISMPosition( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { @@ -675,6 +847,74 @@ static ivas_error unpackISMPosition( const uint8_t *buffer, uint32_t numDataByte } #endif +#ifdef RTP_UPDATES_SA4_134 +static ivas_error packISMPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) +{ + uint32_t nBytes = 0, n; + uint16_t posX, posY, posZ = 0; + const IVAS_PIDATA_ISM_POSITION *ism_position = (const IVAS_PIDATA_ISM_POSITION *) piData; + + *nBytesWritten = 0; + + if ( piData->size != sizeof( IVAS_PIDATA_ISM_POSITION ) ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in ISM POSITION COMPACT PI data" ); + } + + if ( piData->piDataType != IVAS_PI_ISM_POSITION_COMPACT ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in ISM POSITION COMPACT PI data" ); + } + + /* Compact position data is 4 bytes, header is 2 bytes */ + if ( maxDataBytes < 4 * IVAS_MAX_NUM_OBJECTS + 2 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack ISM POSITION COMPACT PI data" ); + } + + buffer[nBytes++] = ( ism_position->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ + buffer[nBytes++] = (uint8_t) ism_position->numObjects * 4; + for ( n = 0; n < ism_position->numObjects; n++ ) + { + posX = (uint16_t) ivasPayload_convertToQ11( ism_position->position[n].x / MAX_PI_COMPACT_POSITION_XY_METERS ); + posY = (uint16_t) ivasPayload_convertToQ11( ism_position->position[n].y / MAX_PI_COMPACT_POSITION_XY_METERS ); + posZ = (uint16_t) ivasPayload_convertToQ10( ism_position->position[n].z / MAX_PI_COMPACT_POSITION_Z_METERS ); + buffer[nBytes++] = (uint8_t) ( posX >> 8 ); + buffer[nBytes++] = (uint8_t) ( ( ( posX & MASK_3BIT ) << 5 ) | posY >> 6 ); + buffer[nBytes++] = (uint8_t) ( ( ( posY & MASK_6BIT ) << 2 ) | posZ >> 8 ); + buffer[nBytes++] = (uint8_t) ( posZ >> 2 ); + } + *nBytesWritten = nBytes; + return IVAS_ERR_OK; +} + +static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) +{ + uint16_t n; + uint32_t compactRead = 0; + IVAS_PIDATA_ISM_POSITION *ism_position = (IVAS_PIDATA_ISM_POSITION *) piData; + + /* Compact position data is 4 bytes */ + if ( numDataBytes % 4 != 0 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack ISM POSITION COMPACT PI data" ); + } + + ism_position->size = sizeof( IVAS_PIDATA_ISM_POSITION ); + ism_position->piDataType = IVAS_PI_ISM_POSITION; + ism_position->numObjects = (uint16_t) numDataBytes / 4; + + for ( n = 0; n < ism_position->numObjects; n++ ) + { + compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); + ism_position->position[n].x = FLOAT_FROM_Q11( compactRead >> 21 ) * MAX_PI_COMPACT_POSITION_XY_METERS; + ism_position->position[n].y = FLOAT_FROM_Q11( ( compactRead >> 10 ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; + ism_position->position[n].z = FLOAT_FROM_Q10( compactRead & MASK_10BIT ) * MAX_PI_COMPACT_POSITION_Z_METERS; + } + return IVAS_ERR_OK; +} +#endif + static ivas_error packDiegetic( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { uint32_t nBytes = 0, n; @@ -752,6 +992,16 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in PI data of type Audio Focus" ); } +#ifdef RTP_UPDATES_SA4_134 + if ( audioFocus->availDirection && audioFocus->availLevel ) + { + packedSize = 5; + } + else if ( audioFocus->availDirection ) + { + packedSize = 4; + } +#else if ( audioFocus->availDirection && audioFocus->availLevel ) { packedSize = 9; @@ -760,6 +1010,7 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 { packedSize = 8; } +#endif else if ( audioFocus->availLevel ) { packedSize = 1; @@ -778,6 +1029,19 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 buffer[nBytes++] = ( audioFocus->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ buffer[nBytes++] = packedSize; +#ifdef RTP_UPDATES_SA4_134 + if ( packedSize == 5 || packedSize == 4 ) + { + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( audioFocus->direction.w ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( audioFocus->direction.x ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( audioFocus->direction.y ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( audioFocus->direction.z ); + } + if ( packedSize == 5 || packedSize == 1 ) + { + buffer[nBytes++] = ( (uint8_t) audioFocus->flvl & MASK_4BIT ) << 4; + } +#else if ( packedSize == 9 || packedSize == 8 ) { nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.w ) ); @@ -789,6 +1053,7 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 { buffer[nBytes++] = ( (uint8_t) audioFocus->flvl & MASK_4BIT ) << 4; } +#endif *nBytesWritten = nBytes; return IVAS_ERR_OK; @@ -798,6 +1063,34 @@ static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDat { IVAS_PIDATA_AUDIO_FOCUS *audioFocus = (IVAS_PIDATA_AUDIO_FOCUS *) piData; +#ifdef RTP_UPDATES_SA4_134 + /* Audio Focus data is either 1, 4 or 5 bytes */ + if ( numDataBytes != 1 && numDataBytes != 4 && numDataBytes != 5 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack PI data of type Audio Focus" ); + } + + piData->size = sizeof( IVAS_PIDATA_AUDIO_FOCUS ); + audioFocus->availDirection = ( numDataBytes >= 4 ); + audioFocus->availLevel = ( numDataBytes == 1 || numDataBytes == 5 ); + + if ( numDataBytes == 1 ) + { + audioFocus->flvl = ( buffer[0] >> 4 ); + } + else + { + audioFocus->direction.w = FLOAT_FROM_Q7( buffer[0] ); + audioFocus->direction.x = FLOAT_FROM_Q7( buffer[1] ); + audioFocus->direction.y = FLOAT_FROM_Q7( buffer[2] ); + audioFocus->direction.z = FLOAT_FROM_Q7( buffer[3] ); + + if ( numDataBytes == 5 ) + { + audioFocus->flvl = ( buffer[4] >> 4 ); + } + } +#else /* Audio Focus data is either 1, 8 or 9 bytes */ if ( numDataBytes != 1 && numDataBytes != 8 && numDataBytes != 9 ) { @@ -824,6 +1117,7 @@ static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDat audioFocus->flvl = ( buffer[8] >> 4 ); } } +#endif return IVAS_ERR_OK; } @@ -1467,6 +1761,9 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packISMGain, /* ISM_GAIN */ packISMOrientation, /* ISM_ORIENTATION */ packISMPosition, /* ISM_POSITION */ +#ifdef RTP_UPDATES_SA4_134 + packISMPositionCompact, /* ISM_POSITION_COMPACT */ +#endif packISMDistanceAttenuation, /* ISM_DISTANCE_ATTENUATION */ packISMDirectivity, /* ISM_DIRECTIVITY */ #else @@ -1483,13 +1780,19 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #else packUnsupportedData, /* DIEGETIC_TYPE */ #endif +#ifdef RTP_UPDATES_SA4_134 + packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ +#else packUnsupportedData, /* RESERVED13 */ +#endif #ifdef RTP_S4_251135_CR26253_0016_REV1 packAudioFocusCommon, /* AUDIO_FOCUS_INDICATION */ #else packUnsupportedData, /* AUDIO_FOCUS_INDICATION */ #endif +#ifndef RTP_UPDATES_SA4_134 packUnsupportedData, /* RESERVED15 */ +#endif #ifdef RTP_S4_251135_CR26253_0016_REV1 packOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ packOrientation, /* HEAD_ORIENTATION */ @@ -1498,7 +1801,11 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #else packListenerPosition, /* LISTENER_POSITION */ #endif +#ifdef RTP_UPDATES_SA4_134 + packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_REQUEST */ +#else packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ +#endif packAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #ifdef PI_LATENCY packPiLatency, /* PI_LATENCY */ @@ -1527,6 +1834,9 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #endif #ifdef REVERSE_ISM_PI_DATA packPosition, /* R_ISM_POSITION */ +#ifdef RTP_UPDATES_SA4_134 + packPositionCompact, /* R_ISM_POSITION_COMPACT */ +#endif packReverseISMDirection, /* R_ISM_DIRECTION */ #else packUnsupportedData, /* R_ISM_POSITION */ @@ -1535,7 +1845,9 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packUnsupportedData, /* RESERVED27 */ packUnsupportedData, /* RESERVED28 */ packUnsupportedData, /* RESERVED29 */ +#ifndef RTP_UPDATES_SA4_134 packUnsupportedData, /* RESERVED30 */ +#endif packNoPiData /* NO_DATA */ }; @@ -1555,6 +1867,9 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackISMGain, /* ISM_GAIN */ unpackISMOrientation, /* ISM_ORIENTATION */ unpackISMPosition, /* ISM_POSITION */ +#ifdef RTP_UPDATES_SA4_134 + unpackISMPositionCompact, /* ISM_POSITION_COMPACT */ +#endif unpackISMDistanceAttenuation, /* ISM_DISTANCE_ATTENUATION */ unpackISMDirectivity, /* ISM_DIRECTIVITY */ #else @@ -1571,13 +1886,19 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #else unpackUnsupportedData, /* DIEGETIC_TYPE */ #endif +#ifdef RTP_UPDATES_SA4_134 + unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ +#else unpackUnsupportedData, /* RESERVED13 */ +#endif #ifdef RTP_S4_251135_CR26253_0016_REV1 unpackAudioFocusCommon, /* AUDIO_FOCUS_INDICATION */ #else unpackUnsupportedData, /* AUDIO_FOCUS_INDICATION */ #endif +#ifndef RTP_UPDATES_SA4_134 unpackUnsupportedData, /* RESERVED15 */ +#endif #ifdef RTP_S4_251135_CR26253_0016_REV1 unpackOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ unpackOrientation, /* HEAD_ORIENTATION */ @@ -1586,7 +1907,11 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #else unpackListenerPosition, /* LISTENER_POSITION */ #endif +#ifdef RTP_UPDATES_SA4_134 + unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_REQUEST */ +#else unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ +#endif unpackAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #ifdef PI_LATENCY unpackPiLatency, /* PI_LATENCY */ @@ -1615,6 +1940,9 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #endif #ifdef REVERSE_ISM_PI_DATA unpackPosition, /* R_ISM_POSITION */ +#ifdef RTP_UPDATES_SA4_134 + unpackPositionCompact, /* R_ISM_POSITION_COMPACT */ +#endif unpackReverseISMDirection, /* R_ISM_DIRECTION */ #else unpackUnsupportedData, /* R_ISM_POSITION */ @@ -1623,10 +1951,48 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* RESERVED27 */ unpackUnsupportedData, /* RESERVED28 */ unpackUnsupportedData, /* RESERVED29 */ +#ifndef RTP_UPDATES_SA4_134 unpackUnsupportedData, /* RESERVED30 */ +#endif unpackNoPiData /* NO_DATA */ }; +#ifdef RTP_UPDATES_SA4_134 +static const uint32_t maxPiDataSize[IVAS_PI_MAX_ID] = { + 4, /* IVAS_PI_SCENE_ORIENTATION */ + 4, /* IVAS_PI_DEVICE_ORIENTATION_COMPENSATED */ + 4, /* IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED */ + 8, /* IVAS_PI_ACOUSTIC_ENVIRONMENT */ + 5, /* IVAS_PI_AUDIO_DESCRIPTION */ + 1, /* IVAS_PI_ISM_NUM */ + 4, /* IVAS_PI_ISM_ID */ + 4, /* IVAS_PI_ISM_GAIN */ + 16, /* IVAS_PI_ISM_ORIENTATION */ + 24, /* IVAS_PI_ISM_POSITION */ + 16, /* IVAS_PI_ISM_POSITION_COMPACT */ + 12, /* IVAS_PI_ISM_DISTANCE_ATTENUATION */ + 8, /* IVAS_PI_ISM_DIRECTIVITY */ + 1, /* IVAS_PI_DIEGETIC_TYPE */ + 2, /* IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ + 5, /* IVAS_PI_AUDIO_FOCUS_INDICATION */ + 4, /* IVAS_PI_PLAYBACK_DEVICE_ORIENTATION */ + 4, /* IVAS_PI_HEAD_ORIENTATION */ + 6, /* IVAS_PI_LISTENER_POSITION */ + 2, /* IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION */ + 5, /* IVAS_PI_AUDIO_FOCUS_REQUEST */ + 4, /* IVAS_PI_PI_LATENCY */ + 1, /* IVAS_PI_R_ISM_ID */ + 1, /* IVAS_PI_R_ISM_GAIN */ + 4, /* IVAS_PI_R_ISM_ORIENTATION */ + 6, /* IVAS_PI_R_ISM_POSITION */ + 4, /* IVAS_PI_R_ISM_POSITION_COMPACT */ + 2, /* IVAS_PI_R_ISM_DIRECTION */ + 0, /* IVAS_PI_RESERVED27 */ + 0, /* IVAS_PI_RESERVED28 */ + 0, /* IVAS_PI_RESERVED29 */ + 0, /* NO_DATA */ +}; +#else static const uint32_t maxPiDataSize[IVAS_PI_MAX_ID] = { 8, /* IVAS_PI_SCENE_ORIENTATION */ 8, /* IVAS_PI_DEVICE_ORIENTATION_COMPENSATED */ @@ -1661,6 +2027,7 @@ static const uint32_t maxPiDataSize[IVAS_PI_MAX_ID] = { 0, /* IVAS_PI_RESERVED30 */ 0, /* IVAS_PI_NO_DATA = 31 */ }; +#endif ivas_error PI_PackData( const IVAS_PIDATA_GENERIC *piData, PIDATA_PACKED *packed, uint8_t pmBits ) { diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 95a2ac9942..1c5d6d8c15 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -61,12 +61,17 @@ typedef enum IVAS_PI_ISM_GAIN, /* gain of each object */ IVAS_PI_ISM_ORIENTATION, /* orientation of each object */ IVAS_PI_ISM_POSITION, /* position of each object */ +#ifdef RTP_UPDATES_SA4_134 + IVAS_PI_ISM_POSITION_COMPACT, /* position of each object in compact representation */ +#endif IVAS_PI_ISM_DISTANCE_ATTENUATION, /* distance attenuation for each object */ IVAS_PI_ISM_DIRECTIVITY, /* directivity of each object */ IVAS_PI_DIEGETIC_TYPE, /* digetic audio indication */ IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION_INDICATION, /* audio suppression indication */ IVAS_PI_AUDIO_FOCUS_INDICATION, /* audio focus indication (direction in Quaternions and/or level) */ +#ifndef RTP_UPDATES_SA4_134 IVAS_PI_RESERVED15, /* reserved */ +#endif /* Reverse direction PI types */ IVAS_PI_PLAYBACK_DEVICE_ORIENTATION, /* orientation of the playback device in quaternions */ @@ -79,11 +84,16 @@ typedef enum IVAS_PI_R_ISM_GAIN, /* editing request for gain factor for received object */ IVAS_PI_R_ISM_ORIENTATION, /* editing request for orientation for received object */ IVAS_PI_R_ISM_POSITION, /* editing request for position for received object */ +#ifdef RTP_UPDATES_SA4_134 + IVAS_PI_R_ISM_POSITION_COMPACT, /* editing request for position for received object in a compact representation*/ +#endif IVAS_PI_R_ISM_DIRECTION, /* editing request for direction for received object */ IVAS_PI_RESERVED27, /* reserved */ IVAS_PI_RESERVED28, /* reserved */ IVAS_PI_RESERVED29, /* reserved */ +#ifndef RTP_UPDATES_SA4_134 IVAS_PI_RESERVED30, /* reserved */ +#endif #endif /* RTP_S4_251135_CR26253_0016_REV1 */ IVAS_PI_NO_DATA = 31, /* Indicates an empty PI data frame */ IVAS_PI_MAX_ID /* Max number of PI data IDs supprted */ @@ -490,6 +500,7 @@ typedef union IVAS_PIDATA_ISM_GAIN ismGain; IVAS_PIDATA_ISM_ORIENTATION ismOrientation; IVAS_PIDATA_ISM_POSITION ismPosition; + IVAS_PIDATA_ISM_POSITION ismPositionCompact; IVAS_PIDATA_ISM_ATTENUATION ismAttenuation; IVAS_PIDATA_ISM_DIRECTIVITY ismDirectivity; IVAS_PIDATA_DIEGETIC digeticIndicator; @@ -514,6 +525,7 @@ typedef union #else IVAS_PIDATA_ISM_EDIT_POSITION ismEditPosition; #endif + IVAS_PIDATA_POSITION ismEditPositionCompact; IVAS_PIDATA_ISM_EDIT_DIRECTION ismEditDirection; #endif /* RTP_S4_251135_CR26253_0016_REV1 */ IVAS_PIDATA_NO_DATA noPiData; diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 5b1bb2be79..f3d286aa58 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -174,12 +174,12 @@ class PIDATAS(str, Enum): ISM_GAIN = "ISM_GAIN" ISM_ORIENTATION = "ISM_ORIENTATION" ISM_POSITION = "ISM_POSITION" + ISM_POSITION_COMPACT = "ISM_POSITION_COMPACT" ISM_DISTANCE_ATTENUATION = "ISM_DISTANCE_ATTENUATION" ISM_DIRECTIVITY = "ISM_DIRECTIVITY" DIEGETIC_TYPE = "DIEGETIC_TYPE" DYNAMIC_AUDIO_SUPPRESSION_INDICATION = "DYNAMIC_AUDIO_SUPPRESSION_INDICATION" AUDIO_FOCUS_INDICATION = "AUDIO_FOCUS_INDICATION" - RESERVED15 = "RESERVED15" PLAYBACK_DEVICE_ORIENTATION = "PLAYBACK_DEVICE_ORIENTATION" HEAD_ORIENTATION = "HEAD_ORIENTATION" LISTENER_POSITION = "LISTENER_POSITION" @@ -190,11 +190,11 @@ class PIDATAS(str, Enum): R_ISM_GAIN = "R_ISM_GAIN" R_ISM_ORIENTATION = "R_ISM_ORIENTATION" R_ISM_POSITION = "R_ISM_POSITION" + R_ISM_POSITION_COMPACT = "R_ISM_POSITION_COMPACT" R_ISM_DIRECTION = "R_ISM_DIRECTION" RESERVED27 = "RESERVED27" RESERVED28 = "RESERVED28" RESERVED29 = "RESERVED29" - RESERVED30 = "RESERVED30" NO_PI_DATA = "NO_PI_DATA" @@ -409,6 +409,9 @@ class ISM_ORIENTATION: class ISM_POSITION: positions: list[POSITION] +class ISM_POSITION_COMPACT: + positions: list[POSITION] + @dataclass class DISTANCE_ATTENUATION: ref_dist: float = 1.0 @@ -727,6 +730,9 @@ cmrLookup = [ ] q15 = lambda x: int(min(32767.0, max(-32768.0, x * 32768.0))) +q11 = lambda x: int(min(2047.0, max(-2048.0, x * 2048.0))) +q10 = lambda x: int(min(1023.0, max(-1024.0, x * 1024.0))) +q7 = lambda x: int(min(127.0, max(-128.0, x * 128.0))) def unpackUnsupported(bitstrm: ConstBitStream, piSize: int) -> any: @@ -748,16 +754,16 @@ def packNoPiData(bitstrm: BitStream, data: any = None): def unpackOrientations(bitstrm: ConstBitStream, piSize: int) -> list[ORIENTATION]: assert ( - piSize % 8 - ) == 0 and piSize <= 32, "Incorrect PI Data Size for list[ORIENTATION]" + piSize % 4 + ) == 0 and piSize <= 16, "Incorrect PI Data Size for list[ORIENTATION]" orientations = list() while piSize > 0: - w = bitstrm.read(16).int / 32768.0 - x = bitstrm.read(16).int / 32768.0 - y = bitstrm.read(16).int / 32768.0 - z = bitstrm.read(16).int / 32768.0 + w = bitstrm.read(8).int / 128.0 + x = bitstrm.read(8).int / 128.0 + y = bitstrm.read(8).int / 128.0 + z = bitstrm.read(8).int / 128.0 orientations.append(ORIENTATION(w, x, y, z)) - piSize -= 8 + piSize -= 4 return orientations @@ -767,10 +773,10 @@ def packOrientations(bitstrm: BitStream, data: any): assert ( type(orientation) == ORIENTATION ), "Orientation PI Data expects a data of type list[ORIENTATION]" - bitstrm.append(f"intbe:16={q15(orientation.w)}") - bitstrm.append(f"intbe:16={q15(orientation.x)}") - bitstrm.append(f"intbe:16={q15(orientation.y)}") - bitstrm.append(f"intbe:16={q15(orientation.z)}") + bitstrm.append(f"intbe:8={q7(orientation.w)}") + bitstrm.append(f"intbe:8={q7(orientation.x)}") + bitstrm.append(f"intbe:8={q7(orientation.y)}") + bitstrm.append(f"intbe:8={q7(orientation.z)}") def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: @@ -797,9 +803,32 @@ def packPositions(bitstrm: BitStream, data: any): bitstrm.append(f"intbe:16={q15(position.y / 327.68)}") bitstrm.append(f"intbe:16={q15(position.z / 327.68)}") +def unpackPositionsCompact(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: + assert piSize <= 16 and (piSize % 4) == 0, "Incorrect PI Data Size for Positions" + positions = list() + while piSize > 0: + x = bitstrm.read(11).int / 100.0 + y = bitstrm.read(11).int / 100.0 + z = bitstrm.read(10).int / 100.0 + positions.append(POSITION(x, y, z)) + piSize -= 4 + return positions + +def packPositionsCompact(bitstrm: BitStream, data: any): + assert type(data) == list, "Compact position PI Data expects a data of type list" + positions = cast(list, data) + assert len(positions) <= 4, "Max one compact position per ISM object" + for position in positions: + assert ( + type(position) == POSITION + ), "Compact position PI Data expects a data of type list[POSITIONS]" + bitstrm.append(f"intbe:11={q11(position.x / 20.48)}") + bitstrm.append(f"intbe:11={q11(position.y / 20.48)}") + bitstrm.append(f"intbe:10={q10(position.z / 10.24)}") + def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: - assert piSize == 8, "Incorrect PI Data Size for ORIENTATION" + assert piSize == 4, "Incorrect PI Data Size for ORIENTATION" orientations = unpackOrientations(bitstrm, piSize) assert len(orientations) == 1 return orientations[0] @@ -825,6 +854,16 @@ def packPosition(bitstrm: BitStream, data: any): position = cast(POSITION, data) packPositions(bitstrm, [position]) +def unpackPositionCompact(bitstrm: ConstBitStream, piSize: int) -> POSITION: + assert piSize == 4, "Incorrect PI Data Size for compact POSITION" + positions = unpackPositionsCompact(bitstrm, piSize) + assert len(positions) == 1 + return positions[0] + +def packPositionCompact(bitstrm: BitStream, data: any): + assert type(data) == POSITION, "Compact position PI Data expects a data of type POSITION" + position = cast(POSITION, data) + packPositionsCompact(bitstrm, [position]) def unpackAudioDescription( bitstrm: ConstBitStream, piSize: int @@ -975,7 +1014,7 @@ def packAcousticEnv(bitstrm: BitStream, data: any): def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: assert ( - piSize == 1 or piSize == 8 or piSize == 9 + piSize == 1 or piSize == 4 or piSize == 5 ), "Incorrect PI Data Size for AUDIO_FOCUS" direction = None level = None @@ -983,8 +1022,8 @@ def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: level = bitstrm.read(4).uint _ = bitstrm.read(4) else: - direction = unpackOrientation(bitstrm, 8) - if piSize == 9: + direction = unpackOrientation(bitstrm, 4) + if piSize == 5: level = bitstrm.read(4).uint _ = bitstrm.read(4) @@ -1166,12 +1205,12 @@ PIDataUnpacker = [ unpackISMGain, # ISM_GAIN unpackOrientations, # ISM_ORIENTATION unpackPositions, # ISM_POSITION + unpackPositionsCompact, # ISM_POSITION_COMPACT unpackISMDistanceAttenuation, # ISM_DISTANCE_ATTENUATION unpackISMDirectivity, # ISM_DIRECTIVITY unpackDiegetic, # DIEGETIC_TYPE unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION_INDICATION unpackAudioFocus, # AUDIO_FOCUS_INDICATION - unpackUnsupported, # RESERVED15 unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION unpackOrientation, # HEAD_ORIENTATION unpackPosition, # LISTENER_POSITION @@ -1182,11 +1221,11 @@ PIDataUnpacker = [ unpackReverseISMGain, # R_ISM_GAIN unpackOrientation, # R_ISM_ORIENTATION unpackPosition, # R_ISM_POSITION + unpackPositionCompact, # R_ISM_POSITION_COMPACT unpackReverseISMDirection, # R_ISM_DIRECTION unpackUnsupported, # RESERVED27 unpackUnsupported, # RESERVED28 unpackUnsupported, # RESERVED29 - unpackUnsupported, # RESERVED30 unpackNoPiData, # NO_DATA ] @@ -1201,12 +1240,12 @@ PIDataPacker = [ packISMGain, # ISM_GAIN packOrientations, # ISM_ORIENTATION packPositions, # ISM_POSITION + packPositionsCompact, # ISM_POSITION_COMPACT packISMDistanceAttenuation, # ISM_DISTANCE_ATTENUATION packISMDirectivity, # ISM_DIRECTIVITY packDiegetic, # DIEGETIC_TYPE packDAS, # DYNAMIC_AUDIO_SUPPRESSION_INDICATION packAudioFocus, # AUDIO_FOCUS_INDICATION - packUnsupported, # RESERVED15 packOrientation, # PLAYBACK_DEVICE_ORIENTATION packOrientation, # HEAD_ORIENTATION packPosition, # LISTENER_POSITION @@ -1217,11 +1256,11 @@ PIDataPacker = [ packReverseISMGain, # R_ISM_GAIN packOrientation, # R_ISM_ORIENTATION packPosition, # R_ISM_POSITION + packPositionCompact, # R_ISM_POSITION_COMPACT packReverseISMDirection, # R_ISM_DIRECTION packUnsupported, # RESERVED27 packUnsupported, # RESERVED28 packUnsupported, # RESERVED29 - packUnsupported, # RESERVED30 packNoPiData, # NO_DATA ] -- GitLab From 5572ee2c80e19693148898627a54c28feb6bfe62 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 18 Nov 2025 23:54:15 -0600 Subject: [PATCH 12/40] Fix pi size parsing, add threshold for orientation parsing --- lib_util/ivas_rtp_payload.c | 6 ++++-- lib_util/ivas_rtp_pi_data.c | 12 ++++++------ tests/rtp/test_rtp.py | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index ce529094f0..6a816f36de 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -1568,6 +1568,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes { bool PF = true; uint32_t nBytes = *numBytes; + bool isFirstPiSize = true; while ( PF ) { @@ -1589,12 +1590,13 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes do { byte = payload->buffer[nBytes++]; - piSize += ( byte & MASK_5BIT ); + piSize += isFirstPiSize ? ( byte & MASK_5BIT ) : byte; if ( nBytes >= payload->length ) { return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading piSize" ); } - } while ( byte == 32 ); + isFirstPiSize = false; + } while ( byte == 255 || piSize == 32 ); #else do { diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index e8a7d05304..de17cf8940 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -111,12 +111,12 @@ static int16_t ivasPayload_convertToQ10( float value ) * * Convert a float value into a Q7 encoded value. *-----------------------------------------------------------------------*/ -static int16_t ivasPayload_convertToQ7( float value ) +static int8_t ivasPayload_convertToQ7( float value ) { value = ( value * 128.0f ); value = value > +128.0f ? +128.0f : value; value = value < -128.0f ? -128.0f : value; - return (int16_t) ( value ); + return (int8_t) ( value ); } #endif @@ -210,10 +210,10 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ #ifdef RTP_UPDATES_SA4_134 buffer[nBytes++] = 4; - buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.w ); - buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.x ); - buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.y ); - buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.z ); + buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.w ); + buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.x ); + buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.y ); + buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.z ); #else buffer[nBytes++] = 8; nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.w ) ); diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 53cb6488c5..3c2550c363 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -322,10 +322,10 @@ def isEqualFrame(refFrame: bytes, dutFrame: bytes): def isEqualOrientation(ref: ORIENTATION, dut: ORIENTATION): - assert abs(ref.w - dut.w) < 0.0001, "Scene Orientation PI Data mismatch in w" - assert abs(ref.x - dut.x) < 0.0001, "Scene Orientation PI Data mismatch in x" - assert abs(ref.y - dut.y) < 0.0001, "Scene Orientation PI Data mismatch in y" - assert abs(ref.z - dut.z) < 0.0001, "Scene Orientation PI Data mismatch in z" + assert abs(ref.w - dut.w) < 0.0079, "Scene Orientation PI Data mismatch in w" + assert abs(ref.x - dut.x) < 0.0079, "Scene Orientation PI Data mismatch in x" + assert abs(ref.y - dut.y) < 0.0079, "Scene Orientation PI Data mismatch in y" + assert abs(ref.z - dut.z) < 0.0079, "Scene Orientation PI Data mismatch in z" def isEqualPosition(ref: POSITION, dut: POSITION): -- GitLab From 92054c0a647c2f27e7ccbabb51db90715c97697c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 00:13:39 -0600 Subject: [PATCH 13/40] Clang format --- lib_util/ivas_rtp_internal.h | 8 +++--- lib_util/ivas_rtp_pi_data.c | 54 ++++++++++++++++++------------------ lib_util/ivas_rtp_pi_data.h | 34 +++++++++++------------ 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index 78f416879f..41d8ae465e 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -75,10 +75,10 @@ enum MASK_BITS #define FLOAT_FROM_Q15( q15Val ) ( (float) ( q15Val ) / 32768.0f ) #ifdef RTP_UPDATES_SA4_134 #define MAX_PI_COMPACT_POSITION_XY_METERS ( 10.24f ) -#define MAX_PI_COMPACT_POSITION_Z_METERS ( 5.12f ) -#define FLOAT_FROM_Q11( q11Val ) ( (float) ( q11Val ) / 2048.0f ) -#define FLOAT_FROM_Q10( q10Val ) ( (float) ( q10Val ) / 1024.0f ) -#define FLOAT_FROM_Q7( q7Val ) ( (float) ( q7Val ) / 128.0f ) +#define MAX_PI_COMPACT_POSITION_Z_METERS ( 5.12f ) +#define FLOAT_FROM_Q11( q11Val ) ( (float) ( q11Val ) / 2048.0f ) +#define FLOAT_FROM_Q10( q10Val ) ( (float) ( q10Val ) / 1024.0f ) +#define FLOAT_FROM_Q7( q7Val ) ( (float) ( q7Val ) / 128.0f ) #endif extern const float mapDSR[1u << NBITS_DSR]; diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index de17cf8940..eccae53c0a 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -1756,13 +1756,13 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packUnsupportedData, /* AUDIO_DESCRIPTION */ #endif /* RTP_S4_251135_CR26253_0016_REV1 */ #ifdef ISM_PI_DATA - packISMNum, /* ISM_NUM */ - packISMID, /* ISM_ID */ - packISMGain, /* ISM_GAIN */ - packISMOrientation, /* ISM_ORIENTATION */ - packISMPosition, /* ISM_POSITION */ + packISMNum, /* ISM_NUM */ + packISMID, /* ISM_ID */ + packISMGain, /* ISM_GAIN */ + packISMOrientation, /* ISM_ORIENTATION */ + packISMPosition, /* ISM_POSITION */ #ifdef RTP_UPDATES_SA4_134 - packISMPositionCompact, /* ISM_POSITION_COMPACT */ + packISMPositionCompact, /* ISM_POSITION_COMPACT */ #endif packISMDistanceAttenuation, /* ISM_DISTANCE_ATTENUATION */ packISMDirectivity, /* ISM_DIRECTIVITY */ @@ -1783,7 +1783,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_UPDATES_SA4_134 packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ #else - packUnsupportedData, /* RESERVED13 */ + packUnsupportedData, /* RESERVED13 */ #endif #ifdef RTP_S4_251135_CR26253_0016_REV1 packAudioFocusCommon, /* AUDIO_FOCUS_INDICATION */ @@ -1799,18 +1799,18 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef REVERSE_ISM_PI_DATA packPosition, /* LISTENER_POSITION */ #else - packListenerPosition, /* LISTENER_POSITION */ + packListenerPosition, /* LISTENER_POSITION */ #endif #ifdef RTP_UPDATES_SA4_134 packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_REQUEST */ #else - packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ + packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ #endif - packAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ + packAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #ifdef PI_LATENCY packPiLatency, /* PI_LATENCY */ #else - packUnsupportedData, /* PI_LATENCY */ + packUnsupportedData, /* PI_LATENCY */ #endif #else packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ @@ -1833,9 +1833,9 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packUnsupportedData, /* R_ISM_ORIENTATION */ #endif #ifdef REVERSE_ISM_PI_DATA - packPosition, /* R_ISM_POSITION */ + packPosition, /* R_ISM_POSITION */ #ifdef RTP_UPDATES_SA4_134 - packPositionCompact, /* R_ISM_POSITION_COMPACT */ + packPositionCompact, /* R_ISM_POSITION_COMPACT */ #endif packReverseISMDirection, /* R_ISM_DIRECTION */ #else @@ -1848,7 +1848,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifndef RTP_UPDATES_SA4_134 packUnsupportedData, /* RESERVED30 */ #endif - packNoPiData /* NO_DATA */ + packNoPiData /* NO_DATA */ }; static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { @@ -1862,13 +1862,13 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* AUDIO_DESCRIPTION */ #endif #ifdef ISM_PI_DATA - unpackISMNum, /* ISM_NUM */ - unpackISMID, /* ISM_ID */ - unpackISMGain, /* ISM_GAIN */ - unpackISMOrientation, /* ISM_ORIENTATION */ - unpackISMPosition, /* ISM_POSITION */ + unpackISMNum, /* ISM_NUM */ + unpackISMID, /* ISM_ID */ + unpackISMGain, /* ISM_GAIN */ + unpackISMOrientation, /* ISM_ORIENTATION */ + unpackISMPosition, /* ISM_POSITION */ #ifdef RTP_UPDATES_SA4_134 - unpackISMPositionCompact, /* ISM_POSITION_COMPACT */ + unpackISMPositionCompact, /* ISM_POSITION_COMPACT */ #endif unpackISMDistanceAttenuation, /* ISM_DISTANCE_ATTENUATION */ unpackISMDirectivity, /* ISM_DIRECTIVITY */ @@ -1887,7 +1887,7 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* DIEGETIC_TYPE */ #endif #ifdef RTP_UPDATES_SA4_134 - unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ + unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_INDICATION */ #else unpackUnsupportedData, /* RESERVED13 */ #endif @@ -1905,18 +1905,18 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef REVERSE_ISM_PI_DATA unpackPosition, /* LISTENER_POSITION */ #else - unpackListenerPosition, /* LISTENER_POSITION */ + unpackListenerPosition, /* LISTENER_POSITION */ #endif #ifdef RTP_UPDATES_SA4_134 unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION_REQUEST */ #else unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ #endif - unpackAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ + unpackAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #ifdef PI_LATENCY unpackPiLatency, /* PI_LATENCY */ #else - unpackUnsupportedData, /* PI_LATENCY */ + unpackUnsupportedData, /* PI_LATENCY */ #endif #else unpackUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ @@ -1939,9 +1939,9 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* R_ISM_ORIENTATION */ #endif #ifdef REVERSE_ISM_PI_DATA - unpackPosition, /* R_ISM_POSITION */ + unpackPosition, /* R_ISM_POSITION */ #ifdef RTP_UPDATES_SA4_134 - unpackPositionCompact, /* R_ISM_POSITION_COMPACT */ + unpackPositionCompact, /* R_ISM_POSITION_COMPACT */ #endif unpackReverseISMDirection, /* R_ISM_DIRECTION */ #else @@ -1954,7 +1954,7 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #ifndef RTP_UPDATES_SA4_134 unpackUnsupportedData, /* RESERVED30 */ #endif - unpackNoPiData /* NO_DATA */ + unpackNoPiData /* NO_DATA */ }; #ifdef RTP_UPDATES_SA4_134 diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 1c5d6d8c15..fa319c6b2e 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -55,14 +55,14 @@ typedef enum IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED, /* orientation of device in unit quaternions (un-compensated) */ IVAS_PI_ACOUSTIC_ENVIRONMENT, /* describe the acoustic environment */ #ifdef RTP_S4_251135_CR26253_0016_REV1 - IVAS_PI_AUDIO_DESCRIPTION, /* audio content description (voice/music/ambiance) */ - IVAS_PI_ISM_NUM, /* Number of objects */ - IVAS_PI_ISM_ID, /* id of each object */ - IVAS_PI_ISM_GAIN, /* gain of each object */ - IVAS_PI_ISM_ORIENTATION, /* orientation of each object */ - IVAS_PI_ISM_POSITION, /* position of each object */ + IVAS_PI_AUDIO_DESCRIPTION, /* audio content description (voice/music/ambiance) */ + IVAS_PI_ISM_NUM, /* Number of objects */ + IVAS_PI_ISM_ID, /* id of each object */ + IVAS_PI_ISM_GAIN, /* gain of each object */ + IVAS_PI_ISM_ORIENTATION, /* orientation of each object */ + IVAS_PI_ISM_POSITION, /* position of each object */ #ifdef RTP_UPDATES_SA4_134 - IVAS_PI_ISM_POSITION_COMPACT, /* position of each object in compact representation */ + IVAS_PI_ISM_POSITION_COMPACT, /* position of each object in compact representation */ #endif IVAS_PI_ISM_DISTANCE_ATTENUATION, /* distance attenuation for each object */ IVAS_PI_ISM_DIRECTIVITY, /* directivity of each object */ @@ -70,7 +70,7 @@ typedef enum IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION_INDICATION, /* audio suppression indication */ IVAS_PI_AUDIO_FOCUS_INDICATION, /* audio focus indication (direction in Quaternions and/or level) */ #ifndef RTP_UPDATES_SA4_134 - IVAS_PI_RESERVED15, /* reserved */ + IVAS_PI_RESERVED15, /* reserved */ #endif /* Reverse direction PI types */ @@ -85,18 +85,18 @@ typedef enum IVAS_PI_R_ISM_ORIENTATION, /* editing request for orientation for received object */ IVAS_PI_R_ISM_POSITION, /* editing request for position for received object */ #ifdef RTP_UPDATES_SA4_134 - IVAS_PI_R_ISM_POSITION_COMPACT, /* editing request for position for received object in a compact representation*/ + IVAS_PI_R_ISM_POSITION_COMPACT, /* editing request for position for received object in a compact representation*/ #endif - IVAS_PI_R_ISM_DIRECTION, /* editing request for direction for received object */ - IVAS_PI_RESERVED27, /* reserved */ - IVAS_PI_RESERVED28, /* reserved */ - IVAS_PI_RESERVED29, /* reserved */ + IVAS_PI_R_ISM_DIRECTION, /* editing request for direction for received object */ + IVAS_PI_RESERVED27, /* reserved */ + IVAS_PI_RESERVED28, /* reserved */ + IVAS_PI_RESERVED29, /* reserved */ #ifndef RTP_UPDATES_SA4_134 - IVAS_PI_RESERVED30, /* reserved */ + IVAS_PI_RESERVED30, /* reserved */ #endif -#endif /* RTP_S4_251135_CR26253_0016_REV1 */ - IVAS_PI_NO_DATA = 31, /* Indicates an empty PI data frame */ - IVAS_PI_MAX_ID /* Max number of PI data IDs supprted */ +#endif /* RTP_S4_251135_CR26253_0016_REV1 */ + IVAS_PI_NO_DATA = 31, /* Indicates an empty PI data frame */ + IVAS_PI_MAX_ID /* Max number of PI data IDs supprted */ } IVAS_PI_TYPE; /* cartesian coordinates (X,Y,Z) in 3D space */ -- GitLab From eb2f69a6cb78fdf06b0c22f02068ffda34274e5a Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 08:28:33 -0600 Subject: [PATCH 14/40] Fix orientation packing, add thersholds to tests --- lib_util/ivas_rtp_pi_data.c | 32 ++++++++++++++++---------------- tests/rtp/test_rtp.py | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index eccae53c0a..8490c907bb 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -210,10 +210,10 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ #ifdef RTP_UPDATES_SA4_134 buffer[nBytes++] = 4; - buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.w ); - buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.x ); - buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.y ); - buffer[nBytes++] = ivasPayload_convertToQ7( orientation->orientation.z ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.w ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.x ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.y ); + buffer[nBytes++] = (uint8_t) ivasPayload_convertToQ7( orientation->orientation.z ); #else buffer[nBytes++] = 8; nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.w ) ); @@ -294,10 +294,10 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte } piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); - orientation->orientation.w = FLOAT_FROM_Q7( buffer[0] ); - orientation->orientation.x = FLOAT_FROM_Q7( buffer[1] ); - orientation->orientation.y = FLOAT_FROM_Q7( buffer[2] ); - orientation->orientation.z = FLOAT_FROM_Q7( buffer[3] ); + orientation->orientation.w = FLOAT_FROM_Q7( (int8_t) buffer[0] ); + orientation->orientation.x = FLOAT_FROM_Q7( (int8_t) buffer[1] ); + orientation->orientation.y = FLOAT_FROM_Q7( (int8_t) buffer[2] ); + orientation->orientation.z = FLOAT_FROM_Q7( (int8_t) buffer[3] ); #else /* Orientation data is 8 bytes */ if ( numDataBytes != 8 ) @@ -333,10 +333,10 @@ static ivas_error unpackISMOrientation( const uint8_t *buffer, uint32_t numDataB for ( n = 0; n < ism_orientation->numObjects; n++ ) { - ism_orientation->orientation[n].w = FLOAT_FROM_Q7( buffer[4 * n] ); - ism_orientation->orientation[n].x = FLOAT_FROM_Q7( buffer[4 * n + 1] ); - ism_orientation->orientation[n].y = FLOAT_FROM_Q7( buffer[4 * n + 2] ); - ism_orientation->orientation[n].z = FLOAT_FROM_Q7( buffer[4 * n + 3] ); + ism_orientation->orientation[n].w = FLOAT_FROM_Q7( (int8_t) buffer[4 * n] ); + ism_orientation->orientation[n].x = FLOAT_FROM_Q7( (int8_t) buffer[4 * n + 1] ); + ism_orientation->orientation[n].y = FLOAT_FROM_Q7( (int8_t) buffer[4 * n + 2] ); + ism_orientation->orientation[n].z = FLOAT_FROM_Q7( (int8_t) buffer[4 * n + 3] ); } #else /* Orientation data is 8 bytes */ @@ -1080,10 +1080,10 @@ static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDat } else { - audioFocus->direction.w = FLOAT_FROM_Q7( buffer[0] ); - audioFocus->direction.x = FLOAT_FROM_Q7( buffer[1] ); - audioFocus->direction.y = FLOAT_FROM_Q7( buffer[2] ); - audioFocus->direction.z = FLOAT_FROM_Q7( buffer[3] ); + audioFocus->direction.w = FLOAT_FROM_Q7( (int8_t) buffer[0] ); + audioFocus->direction.x = FLOAT_FROM_Q7( (int8_t) buffer[1] ); + audioFocus->direction.y = FLOAT_FROM_Q7( (int8_t) buffer[2] ); + audioFocus->direction.z = FLOAT_FROM_Q7( (int8_t) buffer[3] ); if ( numDataBytes == 5 ) { diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 3c2550c363..bb57259eaa 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -392,16 +392,16 @@ def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): assert dut.direction is not None, "Audio Focus PI Data missing direction" if ref.direction is not None and dut.direction is not None: assert ( - abs(ref.direction["w"] - dut.direction.w) < 0.0001 + abs(ref.direction["w"] - dut.direction.w) < 0.0079 ), "Audio Focus PI Data mismatch in direction w" assert ( - abs(ref.direction["x"] - dut.direction.x) < 0.0001 + abs(ref.direction["x"] - dut.direction.x) < 0.0079 ), "Audio Focus PI Data mismatch in direction x" assert ( - abs(ref.direction["y"] - dut.direction.y) < 0.0001 + abs(ref.direction["y"] - dut.direction.y) < 0.0079 ), "Audio Focus PI Data mismatch in direction y" assert ( - abs(ref.direction["z"] - dut.direction.z) < 0.0001 + abs(ref.direction["z"] - dut.direction.z) < 0.0079 ), "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" -- GitLab From 7a3ebeb647711b37c4acffb1624c634a2bb30570 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 10:23:41 -0600 Subject: [PATCH 15/40] Fixes to compact position PI packing and unpacking, add tests for compact position PI --- lib_util/ivas_rtp_internal.h | 2 +- lib_util/ivas_rtp_pi_data.c | 58 ++++++++++++++++++------------------ tests/rtp/ivasrtp.py | 8 ++--- tests/rtp/test_rtp.py | 8 +++++ 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index 41d8ae465e..007730217a 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -76,8 +76,8 @@ enum MASK_BITS #ifdef RTP_UPDATES_SA4_134 #define MAX_PI_COMPACT_POSITION_XY_METERS ( 10.24f ) #define MAX_PI_COMPACT_POSITION_Z_METERS ( 5.12f ) -#define FLOAT_FROM_Q11( q11Val ) ( (float) ( q11Val ) / 2048.0f ) #define FLOAT_FROM_Q10( q10Val ) ( (float) ( q10Val ) / 1024.0f ) +#define FLOAT_FROM_Q9( q9Val ) ( (float) ( q9Val ) / 512.0f ) #define FLOAT_FROM_Q7( q7Val ) ( (float) ( q7Val ) / 128.0f ) #endif diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 8490c907bb..0590232110 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -81,28 +81,28 @@ static int16_t ivasPayload_convertToQ15( float value ) #ifdef RTP_UPDATES_SA4_134 /*-----------------------------------------------------------------------* - * ivasPayload_convertToQ11() + * ivasPayload_convertToQ10() * - * Convert a float value into a Q11 encoded value. + * Convert a float value into a Q10 encoded value. *-----------------------------------------------------------------------*/ -static int16_t ivasPayload_convertToQ11( float value ) +static int16_t ivasPayload_convertToQ10( float value ) { - value = ( value * 2048.0f ); - value = value > +2048.0f ? +2048.0f : value; - value = value < -2048.0f ? -2048.0f : value; + value = ( value * 1024.0f ); + value = value > +1024.0f ? +1024.0f : value; + value = value < -1024.0f ? -1024.0f : value; return (int16_t) ( value ); } /*-----------------------------------------------------------------------* - * ivasPayload_convertToQ10() + * ivasPayload_convertToQ9() * - * Convert a float value into a Q10 encoded value. + * Convert a float value into a Q9 encoded value. *-----------------------------------------------------------------------*/ -static int16_t ivasPayload_convertToQ10( float value ) +static int16_t ivasPayload_convertToQ9( float value ) { - value = ( value * 1024.0f ); - value = value > +1024.0f ? +1024.0f : value; - value = value < -1024.0f ? -1024.0f : value; + value = ( value * 512.0f ); + value = value > +512.0f ? +512.0f : value; + value = value < -512.0f ? -512.0f : value; return (int16_t) ( value ); } @@ -751,9 +751,9 @@ static ivas_error packPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_ buffer[nBytes++] = ( position->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ buffer[nBytes++] = 4; - posX = (uint16_t) ivasPayload_convertToQ11( position->position.x / MAX_PI_COMPACT_POSITION_XY_METERS ); - posY = (uint16_t) ivasPayload_convertToQ11( position->position.y / MAX_PI_COMPACT_POSITION_XY_METERS ); - posZ = (uint16_t) ivasPayload_convertToQ10( position->position.z / MAX_PI_COMPACT_POSITION_Z_METERS ); + posX = (uint16_t) ivasPayload_convertToQ10( position->position.x / MAX_PI_COMPACT_POSITION_XY_METERS ); + posY = (uint16_t) ivasPayload_convertToQ10( position->position.y / MAX_PI_COMPACT_POSITION_XY_METERS ); + posZ = (uint16_t) ivasPayload_convertToQ9( position->position.z / MAX_PI_COMPACT_POSITION_Z_METERS ); buffer[nBytes++] = (uint8_t) ( posX >> 8 ); buffer[nBytes++] = (uint8_t) ( ( ( posX & MASK_3BIT ) << 5 ) | posY >> 6 ); buffer[nBytes++] = (uint8_t) ( ( ( posY & MASK_6BIT ) << 2 ) | posZ >> 8 ); @@ -767,7 +767,7 @@ static ivas_error packPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { IVAS_PIDATA_POSITION *position = (IVAS_PIDATA_POSITION *) piData; - uint32_t compactRead = 0; + int32_t compactRead = 0; /* Compact position data is 4 bytes */ if ( numDataBytes != 4 ) @@ -777,10 +777,10 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData position->size = sizeof( IVAS_PIDATA_POSITION ); - compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); - position->position.x = FLOAT_FROM_Q11( compactRead >> 21 ) * MAX_PI_COMPACT_POSITION_XY_METERS; - position->position.y = FLOAT_FROM_Q11( ( compactRead >> 10 ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; - position->position.z = FLOAT_FROM_Q10( compactRead & MASK_10BIT ) * MAX_PI_COMPACT_POSITION_Z_METERS; + compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); + position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; + position->position.y = FLOAT_FROM_Q10( ( (int16_t) ( compactRead >> 10 ) ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; + position->position.z = FLOAT_FROM_Q9( (int16_t) ( compactRead & MASK_10BIT ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; return IVAS_ERR_OK; } @@ -876,9 +876,9 @@ static ivas_error packISMPositionCompact( const IVAS_PIDATA_GENERIC *piData, uin buffer[nBytes++] = (uint8_t) ism_position->numObjects * 4; for ( n = 0; n < ism_position->numObjects; n++ ) { - posX = (uint16_t) ivasPayload_convertToQ11( ism_position->position[n].x / MAX_PI_COMPACT_POSITION_XY_METERS ); - posY = (uint16_t) ivasPayload_convertToQ11( ism_position->position[n].y / MAX_PI_COMPACT_POSITION_XY_METERS ); - posZ = (uint16_t) ivasPayload_convertToQ10( ism_position->position[n].z / MAX_PI_COMPACT_POSITION_Z_METERS ); + posX = (uint16_t) ivasPayload_convertToQ10( ism_position->position[n].x / MAX_PI_COMPACT_POSITION_XY_METERS ); + posY = (uint16_t) ivasPayload_convertToQ10( ism_position->position[n].y / MAX_PI_COMPACT_POSITION_XY_METERS ); + posZ = (uint16_t) ivasPayload_convertToQ9( ism_position->position[n].z / MAX_PI_COMPACT_POSITION_Z_METERS ); buffer[nBytes++] = (uint8_t) ( posX >> 8 ); buffer[nBytes++] = (uint8_t) ( ( ( posX & MASK_3BIT ) << 5 ) | posY >> 6 ); buffer[nBytes++] = (uint8_t) ( ( ( posY & MASK_6BIT ) << 2 ) | posZ >> 8 ); @@ -891,7 +891,7 @@ static ivas_error packISMPositionCompact( const IVAS_PIDATA_GENERIC *piData, uin static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { uint16_t n; - uint32_t compactRead = 0; + int32_t compactRead = 0; IVAS_PIDATA_ISM_POSITION *ism_position = (IVAS_PIDATA_ISM_POSITION *) piData; /* Compact position data is 4 bytes */ @@ -901,15 +901,15 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD } ism_position->size = sizeof( IVAS_PIDATA_ISM_POSITION ); - ism_position->piDataType = IVAS_PI_ISM_POSITION; + ism_position->piDataType = IVAS_PI_ISM_POSITION_COMPACT; ism_position->numObjects = (uint16_t) numDataBytes / 4; for ( n = 0; n < ism_position->numObjects; n++ ) { - compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); - ism_position->position[n].x = FLOAT_FROM_Q11( compactRead >> 21 ) * MAX_PI_COMPACT_POSITION_XY_METERS; - ism_position->position[n].y = FLOAT_FROM_Q11( ( compactRead >> 10 ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; - ism_position->position[n].z = FLOAT_FROM_Q10( compactRead & MASK_10BIT ) * MAX_PI_COMPACT_POSITION_Z_METERS; + compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); + ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; + ism_position->position[n].y = FLOAT_FROM_Q10( ( (int16_t) ( compactRead >> 10 ) ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; + ism_position->position[n].z = FLOAT_FROM_Q9( (int16_t) ( compactRead & MASK_10BIT ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; } return IVAS_ERR_OK; } diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index f3d286aa58..d07219ac1d 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -730,8 +730,8 @@ cmrLookup = [ ] q15 = lambda x: int(min(32767.0, max(-32768.0, x * 32768.0))) -q11 = lambda x: int(min(2047.0, max(-2048.0, x * 2048.0))) q10 = lambda x: int(min(1023.0, max(-1024.0, x * 1024.0))) +q9 = lambda x: int(min(511.0, max(-512.0, x * 512.0))) q7 = lambda x: int(min(127.0, max(-128.0, x * 128.0))) @@ -822,9 +822,9 @@ def packPositionsCompact(bitstrm: BitStream, data: any): assert ( type(position) == POSITION ), "Compact position PI Data expects a data of type list[POSITIONS]" - bitstrm.append(f"intbe:11={q11(position.x / 20.48)}") - bitstrm.append(f"intbe:11={q11(position.y / 20.48)}") - bitstrm.append(f"intbe:10={q10(position.z / 10.24)}") + bitstrm.append(f"int:11={q10(position.x / 10.24)}") + bitstrm.append(f"int:11={q10(position.y / 10.24)}") + bitstrm.append(f"int:10={q9(position.z / 5.12)}") def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index bb57259eaa..e9417b3675 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -193,6 +193,11 @@ def generatePiData(startTs: int, endTs: int) -> dict: y=random.randint(-32788, 32767) / 100.0, z=random.randint(-32788, 32767) / 100.0, ) + somePositionCompact = lambda: POSITION( + x=random.randint(-1024, 1023) / 100.0, + y=random.randint(-1024, 1023) / 100.0, + z=random.randint(-512, 511) / 100.0, + ) someDesc = lambda: AUDIO_DESCRIPTION( isSpeech=bool(random.getrandbits(1)), isMusic=bool(random.getrandbits(1)), @@ -275,6 +280,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: someISMGains = lambda num_ism : ISM_GAIN(gains=[random.choice([int(random.randint(-24,12)), -128]) for _ in range(num_ism)]) # -128 corresponds to -Inf someISMOrientations = lambda num_ism : [ORIENTATION(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0) for _ in range(num_ism)] someISMPositions = lambda num_ism : [POSITION( x=random.randint(-32788, 32767)/100.0, y=random.randint(-32788, 32767)/100.0, z=random.randint(-32788, 32767)/100.0) for _ in range(num_ism)] + someISMPositionsCompact = lambda num_ism : [POSITION( x=random.randint(-1024, 1023)/100.0, y=random.randint(-1024, 1023)/100.0, z=random.randint(-512, 511)/100.0) for _ in range(num_ism)] someISMDistanceAttenuations = lambda num_ism : [DISTANCE_ATTENUATION(ref_dist=random.randint(1,64)/10.0, max_dist=random.randint(1,64), roll_off=random.randint(0,40)/10.0) for _ in range(num_ism)] someISMDirectivities = lambda num_ism : [DIRECTIVITY(inner_ang=random.randint(0,24)*15, outer_ang=random.randint(0,24)*15, outer_att=random.choice([random.randint(-30,0)*3, -128])) for _ in range(num_ism)] # -128 corresponds to -Inf @@ -301,6 +307,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["ISM_GAIN"] = someISMGains(pidata["ISM_NUM"].num) pidata["ISM_ORIENTATION"] = someISMOrientations(pidata["ISM_NUM"].num) pidata["ISM_POSITION"] = someISMPositions(pidata["ISM_NUM"].num) + pidata["ISM_POSITION_COMPACT"] = someISMPositionsCompact(pidata["ISM_NUM"].num) pidata["ISM_DISTANCE_ATTENUATION"] = someISMDistanceAttenuations(pidata["ISM_NUM"].num) pidata["ISM_DIRECTIVITY"] = someISMDirectivities(pidata["ISM_NUM"].num) @@ -308,6 +315,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["R_ISM_GAIN"] = someReverseISMGain() pidata["R_ISM_ORIENTATION"] = someOrientation() pidata["R_ISM_POSITION"] = somePosition() + pidata["R_ISM_POSITION_COMPACT"] = somePositionCompact() pidata["R_ISM_DIRECTION"] = someReverseISMDirection() data[str(ts)] = pidata return data -- GitLab From b722441236bb25ddb0267ef1d5083f832342b49c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 12:50:02 -0600 Subject: [PATCH 16/40] Fix usan, orientation progreses --- lib_util/ivas_rtp_pi_data.c | 12 ++++++++++-- tests/rtp/ivasrtp.py | 21 ++++++++++++++++++--- tests/rtp/test_rtp.py | 19 +++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 0590232110..779e3fabb8 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -111,12 +111,12 @@ static int16_t ivasPayload_convertToQ9( float value ) * * Convert a float value into a Q7 encoded value. *-----------------------------------------------------------------------*/ -static int8_t ivasPayload_convertToQ7( float value ) +static int16_t ivasPayload_convertToQ7( float value ) { value = ( value * 128.0f ); value = value > +128.0f ? +128.0f : value; value = value < -128.0f ? -128.0f : value; - return (int8_t) ( value ); + return (int16_t) ( value ); } #endif @@ -775,6 +775,14 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack compact position PI data" ); } + int16_t value1 = -100; + int16_t value2 = 100; + uint16_t value1_uint16 = (uint16_t) value1; + uint16_t value2_uint16 = (uint16_t) value2; + + + + position->size = sizeof( IVAS_PIDATA_POSITION ); compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index d07219ac1d..9129616e87 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -43,6 +43,8 @@ import base64 import argparse from pathlib import Path from typing import cast, Optional +import numpy as np +import ctypes NO_REQ = "NO_REQ" @@ -657,6 +659,8 @@ elevationStepSize = 1.417322835 azimuthDirections = [round(x * azimuthStepSize - 180, 3) for x in range(1, 512)] elevationDirections = [round(x * elevationStepSize - 90, 3) for x in range(0, 127)] +MASK_3BIT = 0x7 +MASK_6BIT = 0x3F def mapNearestIndex(table: list, val: float) -> int: for idx, entry in enumerate(table): @@ -822,9 +826,20 @@ def packPositionsCompact(bitstrm: BitStream, data: any): assert ( type(position) == POSITION ), "Compact position PI Data expects a data of type list[POSITIONS]" - bitstrm.append(f"int:11={q10(position.x / 10.24)}") - bitstrm.append(f"int:11={q10(position.y / 10.24)}") - bitstrm.append(f"int:10={q9(position.z / 5.12)}") + #posX = np.uint16(q10(position.x / 10.24)) + #posY = np.uint16(q10(position.y / 10.24)) + #posZ = np.uint16(q9(position.z / 5.12)) + posX = ctypes.c_int16( q10(position.x / 10.24) ).value + posY = ctypes.c_int16( q10(position.y / 10.24) ).value + posZ = ctypes.c_int16( q9(position.z / 5.12) ).value + bitstrm.append(f"uintbe:8={ctypes.c_uint8(posX >> 3).value}") + bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posX & MASK_3BIT) << 5 ) | (posY >> 6)).value}") + bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posY & MASK_6BIT) << 2 ) | (posZ >> 8)).value}") + bitstrm.append(f"uintbe:8={(posZ >> 2)}") + + #bitstrm.append(f"int:11={q10(position.x / 10.24)}") + #bitstrm.append(f"int:11={q10(position.y / 10.24)}") + #bitstrm.append(f"int:10={q9(position.z / 5.12)}") def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index e9417b3675..8eea91c8ee 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -193,10 +193,15 @@ def generatePiData(startTs: int, endTs: int) -> dict: y=random.randint(-32788, 32767) / 100.0, z=random.randint(-32788, 32767) / 100.0, ) + #somePositionCompact = lambda: POSITION( + # x=random.randint(-1024, 1023) / 100.0, + # y=random.randint(-1024, 1023) / 100.0, + # z=random.randint(-512, 511) / 100.0, + #) somePositionCompact = lambda: POSITION( - x=random.randint(-1024, 1023) / 100.0, - y=random.randint(-1024, 1023) / 100.0, - z=random.randint(-512, 511) / 100.0, + x=1.0, + y=1.0, + z=-1.0, ) someDesc = lambda: AUDIO_DESCRIPTION( isSpeech=bool(random.getrandbits(1)), @@ -290,6 +295,11 @@ def generatePiData(startTs: int, endTs: int) -> dict: for ts in range(startTs, endTs, 320): pidata = dict() + + pidata["ISM_NUM"] = someNumISM() + pidata["ISM_POSITION_COMPACT"] = someISMPositionsCompact(pidata["ISM_NUM"].num) + #pidata["R_ISM_POSITION_COMPACT"] = somePositionCompact() + ''' pidata["SCENE_ORIENTATION"] = someOrientation() pidata["DEVICE_ORIENTATION_COMPENSATED"] = someOrientation() pidata["DEVICE_ORIENTATION_UNCOMPENSATED"] = someOrientation() @@ -317,6 +327,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["R_ISM_POSITION"] = somePosition() pidata["R_ISM_POSITION_COMPACT"] = somePositionCompact() pidata["R_ISM_DIRECTION"] = someReverseISMDirection() + ''' data[str(ts)] = pidata return data @@ -758,7 +769,7 @@ def run_rtp_bitstream_tests( elif pitype == "ISM_ORIENTATION": for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): isEqualOrientation(ORIENTATION(**d), r) - elif pitype == "ISM_POSITION": + elif pitype == "ISM_POSITION" or pitype == "ISM_POSITION_COMPACT": for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): isEqualPosition(POSITION(**d), r) elif pitype == "ISM_DISTANCE_ATTENUATION": -- GitLab From 058ada59ba2ce9d40f04fbf29dc0f5d1ef042322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 21:11:53 +0100 Subject: [PATCH 17/40] Updated representation for quaternions for PI data --- lib_com/options.h | 1 + lib_util/ivas_rtp_internal.h | 3 + lib_util/ivas_rtp_pi_data.c | 144 ++++++++++++++++++++++++++++++----- tests/rtp/ivasrtp.py | 44 +++++++---- tests/rtp/test_rtp.py | 54 ++++++------- 5 files changed, 182 insertions(+), 64 deletions(-) diff --git a/lib_com/options.h b/lib_com/options.h index 7aa9f1c915..a71ccde078 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -169,6 +169,7 @@ #define DECODER_FORMAT_SWITCHING /* Re-initialize the decoder when the format/subformat of the incoming stream is changed */ #define RTP_SR_CODEC_FRAME_SIZE_IN_TOC_BYTE /* CR for split rendering codec framesize signalling in Toc Byte*/ #define FIX_SPLIT_RENDERING_ON_DECODER_RESTART /* Re-configure split rendering on decoder restart */ +#define COMPACT_ORIENTATION_PI_DATA /* ################### Start BE switches ################################# */ /* only BE switches wrt selection floating point code */ diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index a550892548..ad89cdb6d8 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -51,6 +51,9 @@ enum MASK_BITS #ifdef REVERSE_ISM_PI_DATA MASK_9BIT = 0x1FF, #endif +#ifdef COMPACT_ORIENTATION_PI_DATA + MASK_10BIT = 0x3FF, +#endif }; diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index b9b7bcc7e4..0a0899790b 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -37,6 +37,9 @@ #endif #ifdef ISM_PI_DATA #include +#ifdef COMPACT_ORIENTATION_PI_DATA +#include "tools.c" +#endif #ifndef min #define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) @@ -79,6 +82,85 @@ static int16_t ivasPayload_convertToQ15( float value ) return (int16_t) ( value ); } +#ifdef COMPACT_ORIENTATION_PI_DATA +static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) +{ + uint32_t nBytes = 0; + float q[4], q_max; + uint16_t max_q_idx, n, k; + uint32_t lWord; + + q[0] = orientation.w; + q[1] = orientation.x; + q[2] = orientation.y; + q[3] = orientation.z; + + max_q_idx = maximumAbs( q, 4, &q_max ); + if ( q[max_q_idx] < 0 ) + { + for ( n = 0; n < 4; n++ ) + { + q[n] = -q[n]; + } + } + lWord = max_q_idx << 30; + + k = 1; + for ( n = 0; n < 4; n++ ) + { + if ( n == max_q_idx ) + { + continue; + } + lWord |= ( ( (int16_t) ( ( q[n] + 1 ) * 1023 * 0.5f ) & MASK_10BIT ) << ( 30 - k * 10 ) ); + k++; + } + + buffer[nBytes++] = ( lWord >> 24 ) & MASK_8BIT; + buffer[nBytes++] = ( lWord >> 16 ) & MASK_8BIT; + buffer[nBytes++] = ( lWord >> 8 ) & MASK_8BIT; + buffer[nBytes++] = (lWord) &MASK_8BIT; + + return nBytes; +} + + +static ivas_error unpackQuaternion( const uint8_t *buffer, IVAS_QUATERNION *orientation ) +{ + uint32_t i, k, lWord; + uint16_t max_q_idx, tmp; + float q[4], qs; + + lWord = ( buffer[0] ) << 24; + lWord |= ( buffer[1] ) << 16; + lWord |= ( buffer[2] ) << 8; + lWord |= buffer[3]; + + max_q_idx = ( lWord >> 30 ) & MASK_2BIT; + k = 1; + qs = 0.0f; + for ( i = 0; i < 4; i++ ) + { + if ( i == max_q_idx ) + { + continue; + } + tmp = ( lWord >> ( 30 - k * 10 ) ) & MASK_10BIT; + q[i] = tmp / 1023.0f * 2.0f - 1; + qs += q[i] * q[i]; + k++; + } + q[max_q_idx] = sqrtf( 1 - qs ); + orientation->w = q[0]; + orientation->x = q[1]; + orientation->y = q[2]; + orientation->z = q[3]; + + return IVAS_ERR_OK; +} + +#endif + static ivas_error packUnsupportedData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { (void) piData; @@ -152,19 +234,22 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in Orientation PI data" ); } - /* Orientation data is 8 bytes, header is 2 bytes */ - if ( maxDataBytes < 8 + 2 ) + /* Orientation data is 4 bytes, header is 2 bytes */ + if ( maxDataBytes < 4 + 2 ) { return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); } buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ - buffer[nBytes++] = 8; + buffer[nBytes++] = 4; +#ifdef COMPACT_ORIENTATION_PI_DATA + nBytes += packQuaternion( orientation->orientation, &buffer[nBytes] ); +#else nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.w ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.x ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.y ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.z ) ); - +#endif *nBytesWritten = nBytes; return IVAS_ERR_OK; } @@ -196,10 +281,14 @@ static ivas_error packISMOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t buffer[nBytes++] = (uint8_t) orientation->numObjects * 8; for ( n = 0; n < orientation->numObjects; n++ ) { +#ifdef COMPACT_ORIENTATION_PI_DATA + nBytes += packQuaternion( orientation->orientation[n], &buffer[nBytes] ); +#else nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].w ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].x ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].y ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation[n].z ) ); +#endif } *nBytesWritten = nBytes; return IVAS_ERR_OK; @@ -210,17 +299,21 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte { IVAS_PIDATA_ORIENTATION *orientation = (IVAS_PIDATA_ORIENTATION *) piData; - /* Orientation data is 8 bytes */ - if ( numDataBytes != 8 ) + /* Orientation data is 4 bytes */ + if ( numDataBytes != 4 ) { return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); +#ifdef COMPACT_ORIENTATION_PI_DATA + unpackQuaternion( buffer, &( orientation->orientation ) ); +#else orientation->orientation.w = FLOAT_FROM_Q15( readInt16( &buffer[0] ) ); orientation->orientation.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ); orientation->orientation.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ); orientation->orientation.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) ); +#endif return IVAS_ERR_OK; } @@ -230,22 +323,26 @@ static ivas_error unpackISMOrientation( const uint8_t *buffer, uint32_t numDataB { IVAS_PIDATA_ISM_ORIENTATION *ism_orientation = (IVAS_PIDATA_ISM_ORIENTATION *) piData; - /* Orientation data is 8 bytes */ + /* Orientation data is 4 bytes */ uint16_t n; - if ( numDataBytes % 8 != 0 ) + if ( numDataBytes % 4 != 0 ) { return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } ism_orientation->size = sizeof( IVAS_PIDATA_ISM_ORIENTATION ); - ism_orientation->numObjects = (uint16_t) numDataBytes / 8; + ism_orientation->numObjects = (uint16_t) numDataBytes / 4; for ( n = 0; n < ism_orientation->numObjects; n++ ) { +#ifdef COMPACT_ORIENTATION_PI_DATA + unpackQuaternion( &buffer[4 * n], &( ism_orientation->orientation[n] ) ); +#else ism_orientation->orientation[n].w = FLOAT_FROM_Q15( readInt16( &buffer[8 * n] ) ); ism_orientation->orientation[n].x = FLOAT_FROM_Q15( readInt16( &buffer[8 * n + 2] ) ); ism_orientation->orientation[n].y = FLOAT_FROM_Q15( readInt16( &buffer[8 * n + 4] ) ); ism_orientation->orientation[n].z = FLOAT_FROM_Q15( readInt16( &buffer[8 * n + 6] ) ); +#endif } for ( ; n < IVAS_MAX_NUM_OBJECTS; n++ ) { @@ -754,11 +851,11 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 if ( audioFocus->availDirection && audioFocus->availLevel ) { - packedSize = 9; + packedSize = 5; } else if ( audioFocus->availDirection ) { - packedSize = 8; + packedSize = 4; } else if ( audioFocus->availLevel ) { @@ -778,14 +875,19 @@ static ivas_error packAudioFocusCommon( const IVAS_PIDATA_GENERIC *piData, uint8 buffer[nBytes++] = ( audioFocus->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ buffer[nBytes++] = packedSize; - if ( packedSize == 9 || packedSize == 8 ) + if ( packedSize == 5 || packedSize == 4 ) { +#ifdef COMPACT_ORIENTATION_PI_DATA + nBytes += packQuaternion( audioFocus->direction, &buffer[nBytes] ); +#else + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.w ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.x ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.y ) ); nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.z ) ); +#endif } - if ( packedSize == 9 || packedSize == 1 ) + if ( packedSize == 5 || packedSize == 1 ) { buffer[nBytes++] = ( (uint8_t) audioFocus->flvl & MASK_4BIT ) << 4; } @@ -798,15 +900,15 @@ static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDat { IVAS_PIDATA_AUDIO_FOCUS *audioFocus = (IVAS_PIDATA_AUDIO_FOCUS *) piData; - /* Audio Focus data is either 1, 8 or 9 bytes */ - if ( numDataBytes != 1 && numDataBytes != 8 && numDataBytes != 9 ) + /* Audio Focus data is either 1, 4 or 5 bytes */ + if ( numDataBytes != 1 && numDataBytes != 4 && numDataBytes != 5 ) { return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack PI data of type Audio Focus" ); } piData->size = sizeof( IVAS_PIDATA_AUDIO_FOCUS ); - audioFocus->availDirection = ( numDataBytes >= 8 ); - audioFocus->availLevel = ( numDataBytes == 1 || numDataBytes == 9 ); + audioFocus->availDirection = ( numDataBytes >= 4 ); + audioFocus->availLevel = ( numDataBytes == 1 || numDataBytes == 5 ); if ( numDataBytes == 1 ) { @@ -814,14 +916,18 @@ static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDat } else { +#ifdef COMPACT_ORIENTATION_PI_DATA + unpackQuaternion( buffer, &( audioFocus->direction ) ); +#else audioFocus->direction.w = FLOAT_FROM_Q15( readInt16( &buffer[0] ) ); audioFocus->direction.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ); audioFocus->direction.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ); audioFocus->direction.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) ); +#endif - if ( numDataBytes == 9 ) + if ( numDataBytes == 5 ) { - audioFocus->flvl = ( buffer[8] >> 4 ); + audioFocus->flvl = ( buffer[4] >> 4 ); } } diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 5b1bb2be79..34305dc9a5 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -748,16 +748,28 @@ def packNoPiData(bitstrm: BitStream, data: any = None): def unpackOrientations(bitstrm: ConstBitStream, piSize: int) -> list[ORIENTATION]: assert ( - piSize % 8 + piSize % 4 ) == 0 and piSize <= 32, "Incorrect PI Data Size for list[ORIENTATION]" orientations = list() + q = [(float)]*4 + qs = 0 while piSize > 0: - w = bitstrm.read(16).int / 32768.0 - x = bitstrm.read(16).int / 32768.0 - y = bitstrm.read(16).int / 32768.0 - z = bitstrm.read(16).int / 32768.0 + max_q_idx = bitstrm.read(2).uint + for i in range(0,4): + if i == max_q_idx: + continue + tmp = bitstrm.read(10).uint + q[i] = (tmp / 1023.0) * 2.0 - 1.0 + qs = qs + q[i]**2 + + q[max_q_idx] = (1 - qs)**0.5 + + w = q[0] + x = q[1] + y = q[2] + z = q[3] orientations.append(ORIENTATION(w, x, y, z)) - piSize -= 8 + piSize -= 4 return orientations @@ -767,10 +779,16 @@ def packOrientations(bitstrm: BitStream, data: any): assert ( type(orientation) == ORIENTATION ), "Orientation PI Data expects a data of type list[ORIENTATION]" - bitstrm.append(f"intbe:16={q15(orientation.w)}") - bitstrm.append(f"intbe:16={q15(orientation.x)}") - bitstrm.append(f"intbe:16={q15(orientation.y)}") - bitstrm.append(f"intbe:16={q15(orientation.z)}") + q = [orientation.w, orientation.x, orientation.y, orientation.z] + max_q = max(q, key=abs) + max_q_idx = q.index(max_q) + if max_q < 0: + q = [-x for x in q] + bitstrm.append(f"uint:2={max_q_idx}") + for i in range(0,4): + if i == max_q_idx: + continue + bitstrm.append(f"uint:10={(int)((q[i]+1)*1023*1/2)}") def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: @@ -799,7 +817,7 @@ def packPositions(bitstrm: BitStream, data: any): def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: - assert piSize == 8, "Incorrect PI Data Size for ORIENTATION" + assert piSize == 4, "Incorrect PI Data Size for ORIENTATION" orientations = unpackOrientations(bitstrm, piSize) assert len(orientations) == 1 return orientations[0] @@ -983,8 +1001,8 @@ def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: level = bitstrm.read(4).uint _ = bitstrm.read(4) else: - direction = unpackOrientation(bitstrm, 8) - if piSize == 9: + direction = unpackOrientation(bitstrm, 4) + if piSize == 5: level = bitstrm.read(4).uint _ = bitstrm.read(4) diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 53cb6488c5..980c5ba820 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -182,12 +182,17 @@ def generateRequests(startTs: int, endTs: int) -> dict: def generatePiData(startTs: int, endTs: int) -> dict: data = dict() - someOrientation = lambda: ORIENTATION( - w=2 * random.random() - 1.0, - x=2 * random.random() - 1.0, - y=2 * random.random() - 1.0, - z=2 * random.random() - 1.0, - ) + def random_unit_quaternion(): + w = 2*random.random() - 1.0 + x = 2*random.random() - 1.0 + y = 2*random.random() - 1.0 + z = 2*random.random() - 1.0 + n = (w*w + x*x + y*y + z*z)**0.5 + if n == 0.0: + return random_unit_quaternion() + return ORIENTATION(w/n, x/n, y/n, z/n) + + someOrientation = lambda: random_unit_quaternion() somePosition = lambda: POSITION( x=random.randint(-32788, 32767) / 100.0, y=random.randint(-32788, 32767) / 100.0, @@ -209,23 +214,8 @@ def generatePiData(startTs: int, endTs: int) -> dict: someDIG = lambda: DIEGETIC_TYPE( isDigetic=[bool(random.getrandbits(1)) for _ in range(random.randint(1, 5))] ) - someAuFocusDirLvl = lambda: AUDIO_FOCUS( - ORIENTATION( - w=2 * random.random() - 1.0, - x=2 * random.random() - 1.0, - y=2 * random.random() - 1.0, - z=2 * random.random() - 1.0, - ), - level=AUDIO_FOCUS_LEVEL(random.randint(0, 15)), - ) - someAuFocusDir = lambda: AUDIO_FOCUS( - ORIENTATION( - w=2 * random.random() - 1.0, - x=2 * random.random() - 1.0, - y=2 * random.random() - 1.0, - z=2 * random.random() - 1.0, - ) - ) + someAuFocusDirLvl = lambda: AUDIO_FOCUS(random_unit_quaternion(),level=AUDIO_FOCUS_LEVEL(random.randint(0, 15)),) + someAuFocusDir = lambda: AUDIO_FOCUS(random_unit_quaternion()) someAuFocusLvl = lambda: AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] someLatency = lambda: PI_LATENCY( @@ -273,7 +263,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: someNumISM = lambda : ISM_NUM(num=random.randint(1, 4)) someISMIds = lambda num_ism : ISM_ID(ids=[int(random.getrandbits(8)) for _ in range(num_ism)]) someISMGains = lambda num_ism : ISM_GAIN(gains=[random.choice([int(random.randint(-24,12)), -128]) for _ in range(num_ism)]) # -128 corresponds to -Inf - someISMOrientations = lambda num_ism : [ORIENTATION(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0) for _ in range(num_ism)] + someISMOrientations = lambda num_ism : [random_unit_quaternion() for _ in range(num_ism)] someISMPositions = lambda num_ism : [POSITION( x=random.randint(-32788, 32767)/100.0, y=random.randint(-32788, 32767)/100.0, z=random.randint(-32788, 32767)/100.0) for _ in range(num_ism)] someISMDistanceAttenuations = lambda num_ism : [DISTANCE_ATTENUATION(ref_dist=random.randint(1,64)/10.0, max_dist=random.randint(1,64), roll_off=random.randint(0,40)/10.0) for _ in range(num_ism)] someISMDirectivities = lambda num_ism : [DIRECTIVITY(inner_ang=random.randint(0,24)*15, outer_ang=random.randint(0,24)*15, outer_att=random.choice([random.randint(-30,0)*3, -128])) for _ in range(num_ism)] # -128 corresponds to -Inf @@ -322,10 +312,10 @@ def isEqualFrame(refFrame: bytes, dutFrame: bytes): def isEqualOrientation(ref: ORIENTATION, dut: ORIENTATION): - assert abs(ref.w - dut.w) < 0.0001, "Scene Orientation PI Data mismatch in w" - assert abs(ref.x - dut.x) < 0.0001, "Scene Orientation PI Data mismatch in x" - assert abs(ref.y - dut.y) < 0.0001, "Scene Orientation PI Data mismatch in y" - assert abs(ref.z - dut.z) < 0.0001, "Scene Orientation PI Data mismatch in z" + assert abs(abs(ref.w) - abs(dut.w)) < 0.01, "Scene Orientation PI Data mismatch in w" + assert abs(abs(ref.x) - abs(dut.x)) < 0.01, "Scene Orientation PI Data mismatch in x" + assert abs(abs(ref.y) - abs(dut.y)) < 0.01, "Scene Orientation PI Data mismatch in y" + assert abs(abs(ref.z) - abs(dut.z)) < 0.01, "Scene Orientation PI Data mismatch in z" def isEqualPosition(ref: POSITION, dut: POSITION): @@ -392,16 +382,16 @@ def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): assert dut.direction is not None, "Audio Focus PI Data missing direction" if ref.direction is not None and dut.direction is not None: assert ( - abs(ref.direction["w"] - dut.direction.w) < 0.0001 + abs(abs(ref.direction["w"]) - abs(dut.direction.w)) < 0.01 ), "Audio Focus PI Data mismatch in direction w" assert ( - abs(ref.direction["x"] - dut.direction.x) < 0.0001 + abs(abs(ref.direction["x"]) - abs(dut.direction.x)) < 0.01 ), "Audio Focus PI Data mismatch in direction x" assert ( - abs(ref.direction["y"] - dut.direction.y) < 0.0001 + abs(abs(ref.direction["y"]) - abs(dut.direction.y)) < 0.01 ), "Audio Focus PI Data mismatch in direction y" assert ( - abs(ref.direction["z"] - dut.direction.z) < 0.0001 + abs(abs(ref.direction["z"]) - abs(dut.direction.z)) < 0.01 ), "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" -- GitLab From 3370c478eb2d02a2218550ed4e2496ab0b801a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 21:47:50 +0100 Subject: [PATCH 18/40] add missing endif --- lib_util/ivas_rtp_pi_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 54cdd1a967..53e59ebe99 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -405,7 +405,7 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte orientation->orientation.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ); orientation->orientation.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ); orientation->orientation.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) ); - +#endif return IVAS_ERR_OK; } -- GitLab From 099ce9c2dcbd3f3129becc8edbcd1a53f07bea45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 21:53:54 +0100 Subject: [PATCH 19/40] disable unused code --- lib_util/ivas_rtp_pi_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 53e59ebe99..e22b3e58de 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -873,10 +873,10 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack compact position PI data" ); } - int16_t value1 = -100; - int16_t value2 = 100; - uint16_t value1_uint16 = (uint16_t) value1; - uint16_t value2_uint16 = (uint16_t) value2; + //int16_t value1 = -100; + //int16_t value2 = 100; + //uint16_t value1_uint16 = (uint16_t) value1; + //uint16_t value2_uint16 = (uint16_t) value2; -- GitLab From b22714ae162e639afe75bb8d5c0e1c70177a81fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 21:58:38 +0100 Subject: [PATCH 20/40] remove unused function --- lib_util/ivas_rtp_pi_data.c | 39 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index e22b3e58de..57dffbd369 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -109,20 +109,6 @@ static int16_t ivasPayload_convertToQ9( float value ) return (int16_t) ( value ); } -/*-----------------------------------------------------------------------* - * ivasPayload_convertToQ7() - * - * Convert a float value into a Q7 encoded value. - *-----------------------------------------------------------------------*/ -static int16_t ivasPayload_convertToQ7( float value ) -{ - value = ( value * 128.0f ); - value = value > +128.0f ? +128.0f : value; - value = value < -128.0f ? -128.0f : value; - return (int16_t) ( value ); -} -#endif - #ifdef COMPACT_ORIENTATION_PI_DATA static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) { @@ -200,6 +186,21 @@ static ivas_error unpackQuaternion( const uint8_t *buffer, IVAS_QUATERNION *orie return IVAS_ERR_OK; } +#else + +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ7() + * + * Convert a float value into a Q7 encoded value. + *-----------------------------------------------------------------------*/ +static int16_t ivasPayload_convertToQ7( float value ) +{ + value = ( value * 128.0f ); + value = value > +128.0f ? +128.0f : value; + value = value < -128.0f ? -128.0f : value; + return (int16_t) ( value ); +} +#endif #endif static ivas_error packUnsupportedData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) @@ -873,12 +874,10 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack compact position PI data" ); } - //int16_t value1 = -100; - //int16_t value2 = 100; - //uint16_t value1_uint16 = (uint16_t) value1; - //uint16_t value2_uint16 = (uint16_t) value2; - - + // int16_t value1 = -100; + // int16_t value2 = 100; + // uint16_t value1_uint16 = (uint16_t) value1; + // uint16_t value2_uint16 = (uint16_t) value2; position->size = sizeof( IVAS_PIDATA_POSITION ); -- GitLab From 406aed598903c98023f778933ebd414d8dee963a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 22:07:51 +0100 Subject: [PATCH 21/40] correct bit mask for 10 and 11 bits --- lib_util/ivas_rtp_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index 007730217a..2ab816fab7 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -52,8 +52,8 @@ enum MASK_BITS MASK_9BIT = 0x1FF, #endif #ifdef RTP_UPDATES_SA4_134 - MASK_10BIT = 0x2FF, - MASK_11BIT = 0x3FF, + MASK_10BIT = 0x3FF, + MASK_11BIT = 0x7FF, #endif }; -- GitLab From 2059fef974183eeeaeb36c510a0308ea93eb6121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Toftg=C3=A5rd?= Date: Wed, 19 Nov 2025 22:29:05 +0100 Subject: [PATCH 22/40] update packPositionsCompact() --- tests/rtp/ivasrtp.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 9129616e87..aec9d4cbe3 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -829,17 +829,17 @@ def packPositionsCompact(bitstrm: BitStream, data: any): #posX = np.uint16(q10(position.x / 10.24)) #posY = np.uint16(q10(position.y / 10.24)) #posZ = np.uint16(q9(position.z / 5.12)) - posX = ctypes.c_int16( q10(position.x / 10.24) ).value - posY = ctypes.c_int16( q10(position.y / 10.24) ).value - posZ = ctypes.c_int16( q9(position.z / 5.12) ).value - bitstrm.append(f"uintbe:8={ctypes.c_uint8(posX >> 3).value}") - bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posX & MASK_3BIT) << 5 ) | (posY >> 6)).value}") - bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posY & MASK_6BIT) << 2 ) | (posZ >> 8)).value}") - bitstrm.append(f"uintbe:8={(posZ >> 2)}") - - #bitstrm.append(f"int:11={q10(position.x / 10.24)}") - #bitstrm.append(f"int:11={q10(position.y / 10.24)}") - #bitstrm.append(f"int:10={q9(position.z / 5.12)}") + # posX = ctypes.c_int16( q10(position.x / 10.24) ).value + # posY = ctypes.c_int16( q10(position.y / 10.24) ).value + # posZ = ctypes.c_int16( q9(position.z / 5.12) ).value + # bitstrm.append(f"uintbe:8={ctypes.c_uint8(posX >> 3).value}") + # bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posX & MASK_3BIT) << 5 ) | (posY >> 6)).value}") + # bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posY & MASK_6BIT) << 2 ) | (posZ >> 8)).value}") + # bitstrm.append(f"uintbe:8={(posZ >> 2)}") + + bitstrm.append(f"int:11={q10(position.x / 10.24)}") + bitstrm.append(f"int:11={q10(position.y / 10.24)}") + bitstrm.append(f"int:10={q9(position.z / 5.12)}") def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: -- GitLab From 4c69474d9ecdfc222ad9f484f78d7481a15fe95d Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 17:14:09 -0600 Subject: [PATCH 23/40] Update unpacking of unpackISMPositionCompact and unpackPositionCompact to support negative ints --- lib_util/ivas_rtp_pi_data.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 779e3fabb8..0c13eac82f 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -775,20 +775,14 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack compact position PI data" ); } - int16_t value1 = -100; - int16_t value2 = 100; - uint16_t value1_uint16 = (uint16_t) value1; - uint16_t value2_uint16 = (uint16_t) value2; - - - - position->size = sizeof( IVAS_PIDATA_POSITION ); compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); - position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; - position->position.y = FLOAT_FROM_Q10( ( (int16_t) ( compactRead >> 10 ) ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; - position->position.z = FLOAT_FROM_Q9( (int16_t) ( compactRead & MASK_10BIT ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; + position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + position->position.y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + position->position.z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ return IVAS_ERR_OK; } @@ -915,9 +909,11 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD for ( n = 0; n < ism_position->numObjects; n++ ) { compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); - ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; - ism_position->position[n].y = FLOAT_FROM_Q10( ( (int16_t) ( compactRead >> 10 ) ) & MASK_11BIT ) * MAX_PI_COMPACT_POSITION_XY_METERS; - ism_position->position[n].z = FLOAT_FROM_Q9( (int16_t) ( compactRead & MASK_10BIT ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; + ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + ism_position->position[n].z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ } return IVAS_ERR_OK; } -- GitLab From c41840bd9c563bbd6d221f313347cfba5be01066 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 17:26:42 -0600 Subject: [PATCH 24/40] Clang format --- lib_util/ivas_rtp_pi_data.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 0c13eac82f..2cf76b801c 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -779,10 +779,10 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ - compactRead = compactRead << 11; /* Discard read bits */ + compactRead = compactRead << 11; /* Discard read bits */ position->position.y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ - compactRead = compactRead << 11; /* Discard read bits */ - position->position.z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + position->position.z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ return IVAS_ERR_OK; } @@ -910,10 +910,10 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD { compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ - compactRead = compactRead << 11; /* Discard read bits */ + compactRead = compactRead << 11; /* Discard read bits */ ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ - compactRead = compactRead << 11; /* Discard read bits */ - ism_position->position[n].z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ + compactRead = compactRead << 11; /* Discard read bits */ + ism_position->position[n].z = FLOAT_FROM_Q9( (int16_t) ( compactRead >> 22 ) ) * MAX_PI_COMPACT_POSITION_Z_METERS; /* Shift preserves sign bit */ } return IVAS_ERR_OK; } -- GitLab From 29be5676cb34b52d14922cf04d5386b365c8654a Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 17:34:33 -0600 Subject: [PATCH 25/40] Enable all rtpdump tests --- tests/rtp/test_rtp.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 8eea91c8ee..b385ab80f2 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -193,15 +193,10 @@ def generatePiData(startTs: int, endTs: int) -> dict: y=random.randint(-32788, 32767) / 100.0, z=random.randint(-32788, 32767) / 100.0, ) - #somePositionCompact = lambda: POSITION( - # x=random.randint(-1024, 1023) / 100.0, - # y=random.randint(-1024, 1023) / 100.0, - # z=random.randint(-512, 511) / 100.0, - #) somePositionCompact = lambda: POSITION( - x=1.0, - y=1.0, - z=-1.0, + x=random.randint(-1024, 1023) / 100.0, + y=random.randint(-1024, 1023) / 100.0, + z=random.randint(-512, 511) / 100.0, ) someDesc = lambda: AUDIO_DESCRIPTION( isSpeech=bool(random.getrandbits(1)), @@ -295,11 +290,6 @@ def generatePiData(startTs: int, endTs: int) -> dict: for ts in range(startTs, endTs, 320): pidata = dict() - - pidata["ISM_NUM"] = someNumISM() - pidata["ISM_POSITION_COMPACT"] = someISMPositionsCompact(pidata["ISM_NUM"].num) - #pidata["R_ISM_POSITION_COMPACT"] = somePositionCompact() - ''' pidata["SCENE_ORIENTATION"] = someOrientation() pidata["DEVICE_ORIENTATION_COMPENSATED"] = someOrientation() pidata["DEVICE_ORIENTATION_UNCOMPENSATED"] = someOrientation() @@ -327,7 +317,6 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["R_ISM_POSITION"] = somePosition() pidata["R_ISM_POSITION_COMPACT"] = somePositionCompact() pidata["R_ISM_DIRECTION"] = someReverseISMDirection() - ''' data[str(ts)] = pidata return data -- GitLab From a626cd33b3b1d0321a077fe1984e6e524c5a1a48 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 17:58:18 -0600 Subject: [PATCH 26/40] Increment buffer after each ISM read --- lib_util/ivas_rtp_pi_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 2cf76b801c..e02ee12c22 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -908,7 +908,7 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD for ( n = 0; n < ism_position->numObjects; n++ ) { - compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); + compactRead = ( (int32_t) buffer[n*4] << 24 ) | ( (int32_t) buffer[n*4 + 1] << 16 ) | ( (int32_t) buffer[n*4 + 2] << 8 ) | ( (int32_t) buffer[n*4 + 3] ); ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ -- GitLab From e93e1dd50cbd90aed60e54a3b0538b518bc69b73 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 19 Nov 2025 18:01:01 -0600 Subject: [PATCH 27/40] Clang --- lib_util/ivas_rtp_pi_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index e02ee12c22..af1143990f 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -908,7 +908,7 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD for ( n = 0; n < ism_position->numObjects; n++ ) { - compactRead = ( (int32_t) buffer[n*4] << 24 ) | ( (int32_t) buffer[n*4 + 1] << 16 ) | ( (int32_t) buffer[n*4 + 2] << 8 ) | ( (int32_t) buffer[n*4 + 3] ); + compactRead = ( (int32_t) buffer[n * 4] << 24 ) | ( (int32_t) buffer[n * 4 + 1] << 16 ) | ( (int32_t) buffer[n * 4 + 2] << 8 ) | ( (int32_t) buffer[n * 4 + 3] ); ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ -- GitLab From 17ea1e5e371a18e63670eb56c7b6b8bcd1b51c1b Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 20:00:06 -0600 Subject: [PATCH 28/40] Remove include of tools.c and write max abs search with loop --- lib_util/ivas_rtp_pi_data.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index dd00716f6c..31ebe1c21c 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -37,9 +37,6 @@ #endif #ifdef ISM_PI_DATA #include -#ifdef COMPACT_ORIENTATION_PI_DATA -#include "tools.c" -#endif #ifndef min #define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) @@ -122,7 +119,17 @@ static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) q[2] = orientation.y; q[3] = orientation.z; - max_q_idx = maximumAbs( q, 4, &q_max ); + max_q_idx = 0; + q_max = (float) fabs( q[0] ); + for ( n = 1; n < 4; n++ ) + { + if ( (float) fabs( q[n] ) > q_max ) + { + q_max = (float) fabs( q[n] ); + max_q_idx = n; + } + } + if ( q[max_q_idx] < 0 ) { for ( n = 0; n < 4; n++ ) -- GitLab From bd58ce5e38060824f5d3695e7cb4ded130ae72f7 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 20:12:42 -0600 Subject: [PATCH 29/40] Use unsigned int for compactRead variable for bit-wise operations --- lib_util/ivas_rtp_pi_data.c | 8 ++++---- tests/rtp/ivasrtp.py | 10 ---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index af1143990f..cd721ca2c8 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -767,7 +767,7 @@ static ivas_error packPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { IVAS_PIDATA_POSITION *position = (IVAS_PIDATA_POSITION *) piData; - int32_t compactRead = 0; + uint32_t compactRead = 0; /* Compact position data is 4 bytes */ if ( numDataBytes != 4 ) @@ -777,7 +777,7 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData position->size = sizeof( IVAS_PIDATA_POSITION ); - compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); + compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ position->position.y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ @@ -893,7 +893,7 @@ static ivas_error packISMPositionCompact( const IVAS_PIDATA_GENERIC *piData, uin static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { uint16_t n; - int32_t compactRead = 0; + uint32_t compactRead = 0; IVAS_PIDATA_ISM_POSITION *ism_position = (IVAS_PIDATA_ISM_POSITION *) piData; /* Compact position data is 4 bytes */ @@ -908,7 +908,7 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD for ( n = 0; n < ism_position->numObjects; n++ ) { - compactRead = ( (int32_t) buffer[n * 4] << 24 ) | ( (int32_t) buffer[n * 4 + 1] << 16 ) | ( (int32_t) buffer[n * 4 + 2] << 8 ) | ( (int32_t) buffer[n * 4 + 3] ); + compactRead = ( (uint32_t) buffer[n * 4] << 24 ) | ( (uint32_t) buffer[n * 4 + 1] << 16 ) | ( (uint32_t) buffer[n * 4 + 2] << 8 ) | ( (uint32_t) buffer[n * 4 + 3] ); ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index aec9d4cbe3..c1ecf77bb0 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -826,16 +826,6 @@ def packPositionsCompact(bitstrm: BitStream, data: any): assert ( type(position) == POSITION ), "Compact position PI Data expects a data of type list[POSITIONS]" - #posX = np.uint16(q10(position.x / 10.24)) - #posY = np.uint16(q10(position.y / 10.24)) - #posZ = np.uint16(q9(position.z / 5.12)) - # posX = ctypes.c_int16( q10(position.x / 10.24) ).value - # posY = ctypes.c_int16( q10(position.y / 10.24) ).value - # posZ = ctypes.c_int16( q9(position.z / 5.12) ).value - # bitstrm.append(f"uintbe:8={ctypes.c_uint8(posX >> 3).value}") - # bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posX & MASK_3BIT) << 5 ) | (posY >> 6)).value}") - # bitstrm.append(f"uintbe:8={ctypes.c_uint8(( (posY & MASK_6BIT) << 2 ) | (posZ >> 8)).value}") - # bitstrm.append(f"uintbe:8={(posZ >> 2)}") bitstrm.append(f"int:11={q10(position.x / 10.24)}") bitstrm.append(f"int:11={q10(position.y / 10.24)}") -- GitLab From e7a10462d7f155c5f93fc4c40c60a5cb35b79961 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 20:39:20 -0600 Subject: [PATCH 30/40] Revert fix on unsigned buffer --- lib_util/ivas_rtp_pi_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 0b9804eddf..31ebe1c21c 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -873,7 +873,7 @@ static ivas_error packPositionCompact( const IVAS_PIDATA_GENERIC *piData, uint8_ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { IVAS_PIDATA_POSITION *position = (IVAS_PIDATA_POSITION *) piData; - uint32_t compactRead = 0; + int32_t compactRead = 0; /* Compact position data is 4 bytes */ if ( numDataBytes != 4 ) @@ -883,7 +883,7 @@ static ivas_error unpackPositionCompact( const uint8_t *buffer, uint32_t numData position->size = sizeof( IVAS_PIDATA_POSITION ); - compactRead = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | ( (uint32_t) buffer[2] << 8 ) | ( (uint32_t) buffer[3] ); + compactRead = ( (int32_t) buffer[0] << 24 ) | ( (int32_t) buffer[1] << 16 ) | ( (int32_t) buffer[2] << 8 ) | ( (int32_t) buffer[3] ); position->position.x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ position->position.y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ @@ -999,7 +999,7 @@ static ivas_error packISMPositionCompact( const IVAS_PIDATA_GENERIC *piData, uin static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) { uint16_t n; - uint32_t compactRead = 0; + int32_t compactRead = 0; IVAS_PIDATA_ISM_POSITION *ism_position = (IVAS_PIDATA_ISM_POSITION *) piData; /* Compact position data is 4 bytes */ @@ -1014,7 +1014,7 @@ static ivas_error unpackISMPositionCompact( const uint8_t *buffer, uint32_t numD for ( n = 0; n < ism_position->numObjects; n++ ) { - compactRead = ( (uint32_t) buffer[n * 4] << 24 ) | ( (uint32_t) buffer[n * 4 + 1] << 16 ) | ( (uint32_t) buffer[n * 4 + 2] << 8 ) | ( (uint32_t) buffer[n * 4 + 3] ); + compactRead = ( (int32_t) buffer[n * 4] << 24 ) | ( (int32_t) buffer[n * 4 + 1] << 16 ) | ( (int32_t) buffer[n * 4 + 2] << 8 ) | ( (int32_t) buffer[n * 4 + 3] ); ism_position->position[n].x = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ compactRead = compactRead << 11; /* Discard read bits */ ism_position->position[n].y = FLOAT_FROM_Q10( (int16_t) ( compactRead >> 21 ) ) * MAX_PI_COMPACT_POSITION_XY_METERS; /* Shift preserves sign bit */ -- GitLab From a524291b074814967a2d3c4b38aba4505cfe6383 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 21:00:29 -0600 Subject: [PATCH 31/40] Fix for usan error --- lib_util/ivas_rtp_pi_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 31ebe1c21c..f9999ac89c 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -165,10 +165,10 @@ static ivas_error unpackQuaternion( const uint8_t *buffer, IVAS_QUATERNION *orie uint16_t max_q_idx, tmp; float q[4], qs; - lWord = ( buffer[0] ) << 24; - lWord |= ( buffer[1] ) << 16; - lWord |= ( buffer[2] ) << 8; - lWord |= buffer[3]; + lWord = ( (uint32_t) buffer[0] ) << 24; + lWord |= ( (uint32_t) buffer[1] ) << 16; + lWord |= ( (uint32_t) buffer[2] ) << 8; + lWord |= (uint32_t) buffer[3]; max_q_idx = ( lWord >> 30 ) & MASK_2BIT; k = 1; -- GitLab From 472474a9e2d20b2736101aa87f043248a296450d Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 21:34:43 -0600 Subject: [PATCH 32/40] Another usan fix --- lib_util/ivas_rtp_pi_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index f9999ac89c..b7ca5a91a4 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -137,7 +137,7 @@ static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) q[n] = -q[n]; } } - lWord = max_q_idx << 30; + lWord = ( (uint32_t) max_q_idx ) << 30; k = 1; for ( n = 0; n < 4; n++ ) -- GitLab From 38cd955b11d9c809673699e845ddbbfa2c1c1e8c Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 22:33:18 -0600 Subject: [PATCH 33/40] Test efficient scaling of Quaternions --- lib_util/ivas_rtp_pi_data.c | 4 ++-- tests/rtp/ivasrtp.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index b7ca5a91a4..425484f47b 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -146,7 +146,7 @@ static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) { continue; } - lWord |= ( ( (int16_t) ( ( q[n] + 1 ) * 1023 * 0.5f ) & MASK_10BIT ) << ( 30 - k * 10 ) ); + lWord |= ( ( (int16_t) ( ( q[n] + 1 / sqrt( 2.0f ) ) * 1023 * sqrt( 2.0f ) ) & MASK_10BIT ) << ( 30 - k * 10 ) ); k++; } @@ -180,7 +180,7 @@ static ivas_error unpackQuaternion( const uint8_t *buffer, IVAS_QUATERNION *orie continue; } tmp = ( lWord >> ( 30 - k * 10 ) ) & MASK_10BIT; - q[i] = tmp / 1023.0f * 2.0f - 1; + q[i] = tmp / 1023.0f * sqrtf( 2.0f ) - 1 / sqrtf( 2.0f ); qs += q[i] * q[i]; k++; } diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index c66bb497fb..9da75180e9 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -769,7 +769,7 @@ def unpackOrientations(bitstrm: ConstBitStream, piSize: int) -> list[ORIENTATION if i == max_q_idx: continue tmp = bitstrm.read(10).uint - q[i] = (tmp / 1023.0) * 2.0 - 1.0 + q[i] = (tmp / 1023.0) * 2.0**0.5 - 1.0 / (2.0**0.5) qs = qs + q[i]**2 q[max_q_idx] = (1 - qs)**0.5 @@ -798,7 +798,7 @@ def packOrientations(bitstrm: BitStream, data: any): for i in range(0,4): if i == max_q_idx: continue - bitstrm.append(f"uint:10={(int)((q[i]+1)*1023*1/2)}") + bitstrm.append(f"uint:10={(int)((q[i]+1/(2**0.5))*1023*(2**0.5))}") def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: -- GitLab From 3416bd7f58cee25c59cc73328a0b777a3fe54c4a Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Wed, 19 Nov 2025 22:41:16 -0600 Subject: [PATCH 34/40] Fix --- lib_util/ivas_rtp_pi_data.c | 2 +- tests/rtp/ivasrtp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 425484f47b..10adfc7ba6 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -146,7 +146,7 @@ static uint32_t packQuaternion( IVAS_QUATERNION orientation, uint8_t *buffer ) { continue; } - lWord |= ( ( (int16_t) ( ( q[n] + 1 / sqrt( 2.0f ) ) * 1023 * sqrt( 2.0f ) ) & MASK_10BIT ) << ( 30 - k * 10 ) ); + lWord |= ( ( (int16_t) ( ( q[n] + 1 / sqrt( 2.0f ) ) * 1023 / sqrt( 2.0f ) ) & MASK_10BIT ) << ( 30 - k * 10 ) ); k++; } diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 9da75180e9..ad54db8d35 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -798,7 +798,7 @@ def packOrientations(bitstrm: BitStream, data: any): for i in range(0,4): if i == max_q_idx: continue - bitstrm.append(f"uint:10={(int)((q[i]+1/(2**0.5))*1023*(2**0.5))}") + bitstrm.append(f"uint:10={(int)((q[i]+1/(2**0.5))*1023/(2**0.5))}") def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: -- GitLab From 07edd3fadc97dfd26ef7e11c1b0f56faea104572 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 20 Nov 2025 10:24:55 +0100 Subject: [PATCH 35/40] [revertme] profile renderer pytest testcase durations --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a1058c34d..416749c1cf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -639,13 +639,13 @@ renderer-pytest-on-merge-request: - exit_code=0 - testcase_timeout=60 - - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_ref --testcase_timeout=$testcase_timeout || exit_code=$? + - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_ref --testcase_timeout=$testcase_timeout --durations=0 || exit_code=$? ### Run test using branch scripts and input - if [ $ref_using_main == 1 ]; then git checkout $source_branch_commit_sha; fi # run test - - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_cut --testcase_timeout=$testcase_timeout || exit_code=$? + - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_cut --testcase_timeout=$testcase_timeout --durations=0 || exit_code=$? - zero_errors=$(cat report-junit.xml | grep -c 'errors="0"') || true - *merge-request-comparison-check -- GitLab From 63c8c6425be66a3b199a705ccac467d792646675 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 21 Nov 2025 09:02:44 +0100 Subject: [PATCH 36/40] fix before_script section in conformance template --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0c5f72c334..253970200c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2214,7 +2214,7 @@ sanitizer-test-osba-planar-hoa3-ism4: - if: $MANUAL_PIPELINE_TYPE == "coverage" timeout: 6 hours before_script: - - !reference [.job-linux, before_script] + - !reference [.test-job-linux-needs-testv-dir, before_script] - set -e - 'trap ''echo "Command failed at line $LINENO: $BASH_COMMAND"'' ERR' - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh -- GitLab From bfee5ea3aae53eb7477add78f1c38e1c5d07846d Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 21 Nov 2025 12:29:38 +0100 Subject: [PATCH 37/40] Revert "[revertme] profile renderer pytest testcase durations" This reverts commit 07edd3fadc97dfd26ef7e11c1b0f56faea104572. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 416749c1cf..1a1058c34d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -639,13 +639,13 @@ renderer-pytest-on-merge-request: - exit_code=0 - testcase_timeout=60 - - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_ref --testcase_timeout=$testcase_timeout --durations=0 || exit_code=$? + - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_ref --testcase_timeout=$testcase_timeout || exit_code=$? ### Run test using branch scripts and input - if [ $ref_using_main == 1 ]; then git checkout $source_branch_commit_sha; fi # run test - - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_cut --testcase_timeout=$testcase_timeout --durations=0 || exit_code=$? + - python3 -m pytest -q --log-level ERROR -n auto -rA --html=report.html --self-contained-html --junit-xml=report-junit.xml tests/renderer/test_renderer.py --create_cut --testcase_timeout=$testcase_timeout || exit_code=$? - zero_errors=$(cat report-junit.xml | grep -c 'errors="0"') || true - *merge-request-comparison-check -- GitLab From a547c24d0a9e36e8e7d32d88e84f814496e8a062 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 21 Nov 2025 12:30:42 +0100 Subject: [PATCH 38/40] teporarily use short test files in renderer_short test for BINAURAL_ROOM_IR --- tests/renderer_short/utils.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/renderer_short/utils.py b/tests/renderer_short/utils.py index 1aa7370f08..bf597947e5 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) @@ -237,6 +240,9 @@ def run_renderer( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: format_to_file = FORMAT_TO_FILE_COMPARETEST + # TODO: temporary change to reduce runtime for BINAURAL_ROOM_IR modes + if out_fmt == "BINAURAL_ROOM_IR": + format_to_file = FORMAT_TO_FILE_SMOKETEST else: format_to_file = FORMAT_TO_FILE_SMOKETEST @@ -771,19 +777,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( -- GitLab From f86b5c9551db04e058caf9c30f3dc88e0e767e34 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Fri, 21 Nov 2025 13:40:51 +0100 Subject: [PATCH 39/40] Revert "teporarily use short test files in renderer_short test for BINAURAL_ROOM_IR" This reverts commit a547c24d0a9e36e8e7d32d88e84f814496e8a062. --- tests/renderer_short/utils.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/renderer_short/utils.py b/tests/renderer_short/utils.py index bf597947e5..1aa7370f08 100644 --- a/tests/renderer_short/utils.py +++ b/tests/renderer_short/utils.py @@ -67,8 +67,7 @@ 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 @@ -107,7 +106,6 @@ 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,7 +113,6 @@ 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) @@ -240,9 +237,6 @@ def run_renderer( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: format_to_file = FORMAT_TO_FILE_COMPARETEST - # TODO: temporary change to reduce runtime for BINAURAL_ROOM_IR modes - if out_fmt == "BINAURAL_ROOM_IR": - format_to_file = FORMAT_TO_FILE_SMOKETEST else: format_to_file = FORMAT_TO_FILE_SMOKETEST @@ -777,19 +771,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( -- GitLab From ff20b9b7b7c9c9c59d4af14e8b38981d1884f4c0 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 24 Nov 2025 09:39:02 +0100 Subject: [PATCH 40/40] run renderer_short in basop compat tests --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a1058c34d..06694e4df5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -273,7 +273,7 @@ basop-ref-compat-stv: extends: - .basop-ci-branch-compat-template variables: - TEST_SUITE: "$SHORT_TEST_SUITE tests/renderer" + TEST_SUITE: "$SHORT_TEST_SUITE tests/renderer_short" TESTCASE_TIMEOUT: $TESTCASE_TIMEOUT_STV_SANITIZERS parallel: matrix: -- GitLab