Commit c1eb02d6 authored by sagnowski's avatar sagnowski
Browse files

Add test scripts for Razel

parent 15559695
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
from typing import Optional

from ivas_razel_runner.include.core import (
    AudioConfig,
    AudioFormat,
    AudioSampleType,
    CustomSpeakerLayoutFile,
    File,
    Files,
    SceneDescriptionFile,
    WavFile,
)
from ivas_razel_runner.include.ivas_rend import IvasRendInputFile


def in_file_for_audio_config(config: AudioConfig | SceneDescriptionFile) -> IvasRendInputFile:
    if config == AudioFormat.MONO:
        return WavFile("scripts/testv/stv48c.wav", True, 48000, 1, AudioSampleType.INT16)
    if config == AudioFormat.STEREO:
        return WavFile("scripts/testv/stvST48c.wav", True, 48000, 2, AudioSampleType.INT16)
    if config == AudioFormat.ISM1:
        return WavFile("scripts/testv/stv1ISM48s.wav", True, 48000, 1, AudioSampleType.INT16)
    if config == AudioFormat.ISM2:
        return WavFile("scripts/testv/stv2ISM48s.wav", True, 48000, 2, AudioSampleType.INT16)
    if config == AudioFormat.ISM3:
        return WavFile("scripts/testv/stv3ISM48s.wav", True, 48000, 3, AudioSampleType.INT16)
    if config == AudioFormat.ISM4:
        return WavFile("scripts/testv/stv4ISM48s.wav", True, 48000, 4, AudioSampleType.INT16)
    if config == AudioFormat.MC_5_1:
        return WavFile("scripts/testv/stv51MC48c.wav", True, 48000, 6, AudioSampleType.INT16)
    if config == AudioFormat.MC_7_1:
        return WavFile("scripts/testv/stv71MC48c.wav", True, 48000, 8, AudioSampleType.INT16)
    if config == AudioFormat.MC_5_1_2:
        return WavFile("scripts/testv/stv512MC48c.wav", True, 48000, 8, AudioSampleType.INT16)
    if config == AudioFormat.MC_5_1_4:
        return WavFile("scripts/testv/stv514MC48c.wav", True, 48000, 10, AudioSampleType.INT16)
    if config == AudioFormat.MC_7_1_4:
        return WavFile("scripts/testv/stv714MC48c.wav", True, 48000, 12, AudioSampleType.INT16)
    if config == AudioFormat.FOA:
        return WavFile("scripts/testv/stvFOA48c.wav", True, 48000, 4, AudioSampleType.INT16)
    if config == AudioFormat.HOA2:
        return WavFile("scripts/testv/stv2OA48c.wav", True, 48000, 9, AudioSampleType.INT16)
    if config == AudioFormat.HOA3:
        return WavFile("scripts/testv/stv3OA48c.wav", True, 48000, 16, AudioSampleType.INT16)
    if config == AudioFormat.MASA1:
        return WavFile("scripts/testv/stv2MASA1TC48c.wav", True, 48000, 1, AudioSampleType.INT16)
    if config == AudioFormat.MASA2:
        return WavFile("scripts/testv/stv2MASA2TC48c.wav", True, 48000, 2, AudioSampleType.INT16)
    if isinstance(config, SceneDescriptionFile):
        return config
    if isinstance(config, CustomSpeakerLayoutFile):
        # Assume test file is 5_1 for now
        assert config.num_channels == AudioFormat.MC_5_1.num_channels
        return WavFile("scripts/testv/stv51MC48c.wav", True, 48000, 6, AudioSampleType.INT16)

    raise RuntimeError(f"No input file for audio config {config}")


def md_files_for_audio_config(config: AudioConfig | SceneDescriptionFile) -> Optional[Files]:
    if config == AudioFormat.ISM1:
        return [File("scripts/testv/stvISM1.csv", True)]
    if config == AudioFormat.ISM2:
        return [File("scripts/testv/stvISM1.csv", True), File("scripts/testv/stvISM2.csv", True)]
    if config == AudioFormat.ISM3:
        return [
            File("scripts/testv/stvISM1.csv", True),
            File("scripts/testv/stvISM2.csv", True),
            File("scripts/testv/stvISM3.csv", True),
        ]
    if config == AudioFormat.ISM4:
        return [
            File("scripts/testv/stvISM1.csv", True),
            File("scripts/testv/stvISM2.csv", True),
            File("scripts/testv/stvISM3.csv", True),
            File("scripts/testv/stvISM4.csv", True),
        ]
    if config == AudioFormat.MASA1:
        return [File("scripts/testv/stv2MASA1TC48c.met", True)]
    if config == AudioFormat.MASA2:
        return [File("scripts/testv/stv2MASA2TC48c.met", True)]

    return None
+177 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3

import itertools
import logging
from pathlib import Path

from ivas_files_map_for_razel import in_file_for_audio_config, md_files_for_audio_config
from ivas_razel_runner.include.core import (
    AudioFile,
    AudioFormat,
    CustomSpeakerLayoutFile,
    File,
    FileFormat,
    SceneDescriptionFile,
    WavDiffArgs,
    WavFile,
)
from ivas_razel_runner.include.ivas_rend import IvasRend, IvasRendConfig, UnsupportedConfigWarning
from ivas_razel_runner.include.sox import Sox
from razel import Razel

#######################################################

BINAURAL_FORMATS = [
    AudioFormat.BINAURAL,
    AudioFormat.BINAURAL_ROOM_IR,
    AudioFormat.BINAURAL_ROOM_REVERB,
]
DEFAULT_CUSTOM_SPEAKER_LAYOUT = CustomSpeakerLayoutFile("scripts/ls_layouts/cicp6.txt", 6)
DEFAULT_SCENE_DESCRIPTION_FILE = SceneDescriptionFile("_dev/in/scene_ism2.txt", in_file_for_audio_config(AudioFormat.ISM2))  # type: ignore

#######################################################

INPUT_FORMATS = IvasRend.get_valid_in_audio_configs(DEFAULT_CUSTOM_SPEAKER_LAYOUT, DEFAULT_SCENE_DESCRIPTION_FILE)
OUTPUT_FORMATS = IvasRend.get_valid_out_audio_configs(DEFAULT_CUSTOM_SPEAKER_LAYOUT)
HEAD_ROTATION_FILES = [File("scripts/testv/headrot_case00_3000_q.csv", True), None]
SAMPLING_RATES = [48000]  # TODO(sgi): use others

#######################################################

KNOWN_BROKEN_BASELINE_CONFIGS = [
    lambda config: config.in_audio_config == AudioFormat.MASA1,
    lambda config: isinstance(config.in_audio_config, SceneDescriptionFile),
    lambda config: config.in_audio_config == AudioFormat.MASA2
    and isinstance(config.out_audio_config, CustomSpeakerLayoutFile),
    lambda config: config.in_audio_config == AudioFormat.MASA2
    and config.out_audio_config in [AudioFormat.BINAURAL_ROOM_IR, AudioFormat.BINAURAL_ROOM_REVERB],
]
KNOWN_CLIPPING_CONFIGS = [
    lambda config: config.in_audio_config
    in [AudioFormat.MC_5_1_2, AudioFormat.MC_5_1_4, AudioFormat.MC_5_1, AudioFormat.MC_7_1_4, AudioFormat.MC_7_1]
    and config.out_audio_config in [AudioFormat.MONO, AudioFormat.FOA, AudioFormat.HOA2, AudioFormat.HOA3],
    lambda config: isinstance(config.in_audio_config, CustomSpeakerLayoutFile)
    and config.out_audio_config
    in [AudioFormat.MONO, AudioFormat.STEREO, AudioFormat.FOA, AudioFormat.HOA2, AudioFormat.HOA3],
    lambda config: config.in_audio_config
    in [
        AudioFormat.MC_5_1,
        AudioFormat.MC_5_1_2,
        AudioFormat.MC_5_1_4,
        AudioFormat.MC_7_1,
        AudioFormat.MC_7_1_4,
        AudioFormat.FOA,
        AudioFormat.HOA2,
        AudioFormat.HOA3,
    ]
    and config.out_audio_config in BINAURAL_FORMATS,
]
NON_BE_TO_REF_WITH_5MS_FRAMING = [
    lambda config: config.in_audio_config
    in [
        AudioFormat.ISM1,
        AudioFormat.ISM2,
        AudioFormat.ISM3,
        AudioFormat.ISM4,
    ]
    and config.out_audio_config != AudioFormat.MONO,
    lambda config: config.in_audio_config
    in [
        AudioFormat.MASA1,
        AudioFormat.MASA2,
    ],
]
NON_BE_TO_REF_WITH_20MS_FRAMING = [
    lambda config: config.out_audio_config in BINAURAL_FORMATS and config.head_rotation is not None,
]

#######################################################


def main():
    root_workspace = Path(__file__).parent / ".." / ".."
    Razel.init(str(root_workspace))
    logging.basicConfig(level=logging.INFO)

    rend_cut = IvasRend(str(root_workspace / "IVAS_rend"))
    rend_ref = IvasRend(str(root_workspace / "IVAS_rend_ref_5ms"))
    sox = Sox("sox")
    AudioFile.set_audio_conversion_tool(sox)

    # Define exceptional configs - order matters here!
    rend_ref.add_support_check_for_nonstandard_configs(match_broken_config)
    rend_cut.add_support_check_for_nonstandard_configs(match_broken_config)
    rend_ref.add_support_check_for_nonstandard_configs(
        # Reference can do binaural rendering with 20 ms frames
        lambda config: config.out_audio_config in BINAURAL_FORMATS
        and config.in_audio_config not in [AudioFormat.MONO, AudioFormat.STEREO]
        and not config.framing_5ms
    )

    for in_format, out_format, head_rotation_file in itertools.product(
        INPUT_FORMATS, OUTPUT_FORMATS, HEAD_ROTATION_FILES
    ):
        in_file = in_file_for_audio_config(in_format)
        md_files = md_files_for_audio_config(in_format)
        config = IvasRendConfig(in_format, out_format, md_files, head_rotation=head_rotation_file, framing_5ms=False)

        if any(match(config) for match in KNOWN_CLIPPING_CONFIGS):
            config.input_gain = 0.2

        # These files' length is not a multiple of 5ms, which causes different output lengths with 5ms framing on/off
        if in_file.basename in ["stvFOA48c.wav", "stv2OA48c.wav", "stv3OA48c.wav"]:
            assert isinstance(in_file, WavFile)
            in_file = in_file.trim(0, 20)

        # Run REF 20ms
        try:
            out_20ms_ref = rend_ref.render(in_file, config, FileFormat.WAV)
        except UnsupportedConfigWarning as e:
            logging.info("%s Skipping %s", e.reason, config)
            out_20ms_ref = None

        # Run CUT 20ms
        try:
            out_20ms_cut = rend_cut.render(in_file, config, FileFormat.WAV)
        except UnsupportedConfigWarning as e:
            logging.info("%s Skipping %s", e.reason, config)
            out_20ms_cut = None

        if out_20ms_ref and out_20ms_cut:
            if any(match(config) for match in NON_BE_TO_REF_WITH_20MS_FRAMING):
                assert isinstance(out_20ms_cut, WavFile)
                out_20ms_cut.should_not_equal(out_20ms_ref)
                out_20ms_cut.should_be_similar_to(out_20ms_ref, WavDiffArgs(mld_max=0.01))
            else:
                out_20ms_cut.should_equal(out_20ms_ref)

        # Run CUT 5ms
        config.framing_5ms = True
        try:
            out_5ms_cut = rend_cut.render(in_file, config, FileFormat.WAV)
        except UnsupportedConfigWarning as e:
            logging.info("%s Skipping %s", e.reason, config)
            out_5ms_cut = None

        if out_5ms_cut and out_20ms_ref:
            if any(match(config) for match in NON_BE_TO_REF_WITH_5MS_FRAMING):
                assert isinstance(out_5ms_cut, WavFile)
                out_5ms_cut.should_not_equal(out_20ms_ref)
                out_5ms_cut.should_be_similar_to(
                    out_20ms_ref, WavDiffArgs(mld_max=16)
                )  # TODO(sgi): also test ISM with no metadata (should be BE)
            else:
                out_5ms_cut.should_equal(out_20ms_ref)

    Razel.instance().run(["exec", "-k"])


def match_broken_config(config):
    if any(match(config) for match in KNOWN_BROKEN_BASELINE_CONFIGS):
        raise UnsupportedConfigWarning("Known broken config.")

    return False


if __name__ == "__main__":
    main()