diff --git a/tests/test_be_ambi_converter_fixed_to_float.py b/tests/test_be_ambi_converter_fixed_to_float.py new file mode 100644 index 0000000000000000000000000000000000000000..aa4202d3d1a90a85978b3da31c3d13e1a9c7d64c --- /dev/null +++ b/tests/test_be_ambi_converter_fixed_to_float.py @@ -0,0 +1,126 @@ +import pytest +import subprocess +import itertools +import sys +from enum import Enum +from pathlib import Path +from tempfile import TemporaryDirectory + +HERE = Path(__file__).absolute().parent +TESTV_DIR = HERE.parent / "scripts/testv" +OUTPUT_FOLDER_IF_KEEP_FILES = HERE.joinpath("output-ambi_converter-be") + +sys.path.append(str(HERE.parent / "scripts")) +from pyaudio3dtools import audiofile, audioarray + + +class AMBI_CONVENTION(int, Enum): + ACN_SN3D = 0 + ACN_N3D = 1 + FUMA_MAXN = 2 + FUMA_FUMA = 3 + SID_SN3D = 4 + SID_N3D = 5 + + +def run_ambi_converter( + bin_path: Path, + infile: Path, + outfile: str, + convention_in: AMBI_CONVENTION, + convention_out: AMBI_CONVENTION, +): + cmd = [ + str(bin_path), + str(infile), + outfile, + f"{int(convention_in)}", + f"{int(convention_out)}", + ] + print(" ".join(cmd)) + + p = subprocess.run(cmd, capture_output=True) + if p.returncode != 0: + msg = f"{p.stdout.decode('utf8')}\n{p.stderr.decode('utf8')}" + pytest.fail(f"Ambisonics converter run failed:\n{msg}") + + +# test all ambisonics orders from 1 to 3 +INPUT_CH_NUM = [4, 9, 16] +INPUT_FS = [16, 32, 48] +INPUT_FILES = [ + TESTV_DIR / f"spectral_test_{ch}ch_{fs}kHz.wav" + for ch in INPUT_CH_NUM + for fs in INPUT_FS +] +CONVENTIONS = [c for c in AMBI_CONVENTION] +AMBI_CONVERTER_PATH_FLOAT = HERE.parent / "ambi_converter_flt" +AMBI_CONVERTER_PATH_FIXED = HERE.parent / "ambi_converter_fx" +THRESHOLD_FAIL = 1 + +CONVENTIONS_FULL_COMBI = list(itertools.product(CONVENTIONS, CONVENTIONS)) +CONVENTIONS_TEST_PARAMS = [ + (c_in.value, c_out.value) for c_in, c_out in CONVENTIONS_FULL_COMBI +] +CONVENTIONS_TEST_PARAMS_IDS = [ + f"{c_in.name}-to-{c_out.name}" for c_in, c_out in CONVENTIONS_FULL_COMBI +] + + +@pytest.mark.parametrize("infile", INPUT_FILES, ids=[p.name for p in INPUT_FILES]) +@pytest.mark.parametrize( + "convention_in,convention_out", + CONVENTIONS_TEST_PARAMS, + ids=CONVENTIONS_TEST_PARAMS_IDS, +) +def test_ambi_converter( + infile: Path, + convention_in: AMBI_CONVENTION, + convention_out: AMBI_CONVENTION, + keep_files, + # needs to be passed to correctly report errors + test_info, +): + if ( + convention_out != AMBI_CONVENTION.ACN_SN3D + and convention_in != AMBI_CONVENTION.ACN_SN3D + ): + pytest.xfail("One of in and out convention needs to be ACN_SN3D") + + if keep_files: + OUTPUT_FOLDER_IF_KEEP_FILES.mkdir(exist_ok=True, parents=True) + + with TemporaryDirectory() as tmp_dir: + output_dir = OUTPUT_FOLDER_IF_KEEP_FILES if keep_files else tmp_dir + + outfile_base = Path(output_dir) / ( + infile.stem + f"-{str(convention_in)}-to-{str(convention_out)}" + ) + + outfile_flt = str(outfile_base) + "-flt.wav" + outfile_fx = str(outfile_base) + "-fx.wav" + + run_ambi_converter( + AMBI_CONVERTER_PATH_FLOAT, + infile, + outfile_flt, + convention_in, + convention_out, + ) + + run_ambi_converter( + AMBI_CONVERTER_PATH_FIXED, + infile, + outfile_fx, + convention_in, + convention_out, + ) + + s_flt, _ = audiofile.readfile(outfile_flt) + s_fx, _ = audiofile.readfile(outfile_fx) + + cmp_result = audioarray.compare(s_flt, s_fx, fs=48000, per_frame=False) + if abs(cmp_result["max_abs_diff"]) > THRESHOLD_FAIL: + pytest.fail( + f"Difference between float and fixed ambi_converter output found! Max abs diff: {cmp_result['max_abs_diff']}" + )