Commit c3d0a0e9 authored by emerit's avatar emerit
Browse files

add missing files

parent 285abd86
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -1282,7 +1282,7 @@ int generate_crend_ivas_tables_from_sofa( const char *file_path, ConfigReader *c
        ivas_get_hrtf_lens( ivas_hrtf, &hrtf_data_flt, frame_len );
        if ( sample_rates[indSR] != DEFAULT_SAMPLERATE )
        {
            hrtf_data.latency_s = latency_48k_optim;
            hrtf_data_flt.latency_s = latency_48k_optim;
        }

        if ( ( hrtf_data_flt.num_iterations[0][0] > ( frame_len_ms == 5 ? NUM_ITERATIONS_TO_ALLOW_OPTIM_5_MS : NUM_ITERATIONS_TO_ALLOW_OPTIM_20_MS ) ) && ( cfgReader->optimize == 1 ) )
@@ -1310,7 +1310,7 @@ int generate_crend_ivas_tables_from_sofa( const char *file_path, ConfigReader *c

        if ( sample_rates[indSR] == DEFAULT_SAMPLERATE )
        {
            latency_48k_optim = hrtf_data.latency_s;
            latency_48k_optim = hrtf_data_flt.latency_s;
        }

        hrtf_data.index_frequency_max_diffuse = hrtf_data_flt.index_frequency_max_diffuse;
@@ -2481,7 +2481,7 @@ void update_c_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg, const int
    fprintf( fp, "\n};" );

    /*   float *pOut_to_bin_diffuse_re[BINAURAL_CHANNELS];*/
    if ( hrtf->pOut_to_bin_diffuse_re[0] == NULL )
    if ( hrtf->pOut_to_bin_diffuse_re_dyn[0] == NULL )
    {
        fprintf( fp, "\nconst Word16 *%s_%s_coeff_diffuse_re_%2dkHz[BINAURAL_CHANNELS]={NULL,NULL};", DECLARATION_NAME, lscfg.name, samplerate / 1000 );
    }
@@ -2495,7 +2495,7 @@ void update_c_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg, const int
    }

    /*   float *pOut_to_bin_diffuse_im[BINAURAL_CHANNELS];*/
    if ( hrtf->pOut_to_bin_diffuse_im[0] == NULL )
    if ( hrtf->pOut_to_bin_diffuse_im_dyn[0] == NULL )
    {
        fprintf( fp, "\nconst Word16 *%s_%s_coeff_diffuse_im_%2dkHz[BINAURAL_CHANNELS]={NULL,NULL};", DECLARATION_NAME, lscfg.name, samplerate / 1000 );
    }
@@ -2654,7 +2654,7 @@ void update_h_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg, const int
    fprintf( fp, "\nextern const Word16 %s_%s_inv_diffuse_weight_%2dkHz[BINAURAL_CHANNELS][%s];", DECLARATION_NAME, lscfg.name, samplerate / 1000, lscfg.output_config_num_channel_name );

    /*   uint16_t *pIndex_frequency_max_diffuse[BINAURAL_CHANNELS];*/
    if ( hrtf->pIndex_frequency_max_diffuse[0] == NULL )
    if ( hrtf->pIndex_frequency_max_diffuse_dyn[0] == NULL )
    {
        fprintf( fp, "\nextern const Word16 *%s_%s_pIndex_frequency_max_diffuse_%2dkHz[BINAURAL_CHANNELS];", DECLARATION_NAME, lscfg.name, samplerate / 1000 );
    }
@@ -2669,7 +2669,7 @@ void update_h_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg, const int
    fprintf( fp, "\nextern const Word16 %s_%s_coeff_im_%2dkHz[%s][BINAURAL_CHANNELS][%u];", DECLARATION_NAME, lscfg.name, samplerate / 1000, lscfg.output_config_num_channel_name, maxTotalNumFreqSampPerIterations );

    /*   float *pOut_to_bin_diffuse_re_fx[BINAURAL_CHANNELS];*/
    if ( hrtf->pOut_to_bin_diffuse_re[0] == NULL )
    if ( hrtf->pOut_to_bin_diffuse_re_dyn[0] == NULL )
    {
        fprintf( fp, "\nextern Word16 *%s_%s_coeff_diffuse_re_%2dkHz[BINAURAL_CHANNELS];", DECLARATION_NAME, lscfg.name, samplerate / 1000 );
    }
@@ -2679,7 +2679,7 @@ void update_h_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg, const int
    }

    /*   float *pOut_to_bin_diffuse_im_fx[BINAURAL_CHANNELS];*/
    if ( hrtf->pOut_to_bin_diffuse_im[0] == NULL )
    if ( hrtf->pOut_to_bin_diffuse_im_dyn[0] == NULL )
    {
        fprintf( fp, "\nextern Word16 *%s_%s_coeff_diffuse_im_%2dkHz[BINAURAL_CHANNELS];", DECLARATION_NAME, lscfg.name, samplerate / 1000 );
    }
@@ -2855,7 +2855,7 @@ ivas_error make_fx_be( HRTFS_DATA_flt *hrtf_flt, HRTFS_DATA *hrtf, struct ivas_l

    maxDiff = 0;

    if ( hrtf->latency_s > 0 )
    if ( hrtf_flt->latency_s > 0 )
    {
        hrtf->factor_Q_latency_s = (int16_t) floorf( 31.f - logf( hrtf_flt->latency_s ) / logf( 2.f ) );
    }
@@ -2871,7 +2871,7 @@ ivas_error make_fx_be( HRTFS_DATA_flt *hrtf_flt, HRTFS_DATA *hrtf, struct ivas_l
    tmp = hrtf_flt->latency_s;
    hrtf->latency_s = float2int32_t( hrtf_flt->latency_s, hrtf->factor_Q_latency_s );
    hrtf_flt->latency_s = (float) hrtf->latency_s * powf( 2.f, -1.f * (float) hrtf->factor_Q_latency_s );
    maxDiff = fabsf( hrtf->latency_s - tmp );
    maxDiff = fabsf( hrtf_flt->latency_s - tmp );

    maxVal = 0;
    for ( iIR = 0; iIR < hrtf_flt->max_num_ir; iIR++ )
@@ -3341,7 +3341,7 @@ ivas_error write_binary_file( HRTFS_DATA *hrtf, struct ivas_layout_config lscfg,
}

/*---------------------------------------------------------------------*
 * make_fx_be();
 * make_reverb_fx_be();
 *---------------------------------------------------------------------*/
ivas_error make_reverb_fx_be( float *pEner_l, float *pEner_r, float *pCoherence, const int32_t samplerate, int16_t *factorQ )
{
+1 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#define IVAS_SOFA_MAX_VAL_I      ( 1 )
#define IVAS_MAX_HRTF_LATENCY_MS ( 0.1 ) /* max hrtf latency */

#define GAIN_LFE 1.88364911f /* Gain applied to LFE during renderering */

typedef struct ivas_hrtfs_structure_flt
{

tests/cmp_pcm.py

100755 → 100644
+92 −17
Original line number Diff line number Diff line
@@ -3,6 +3,11 @@
import argparse
import os
import sys
import tempfile
import re
import subprocess
from pathlib import Path
from typing import Optional

THIS_PATH = os.path.join(os.getcwd(), __file__)
sys.path.append(os.path.join(os.path.dirname(THIS_PATH), "../scripts"))
@@ -10,9 +15,21 @@ sys.path.append(os.path.join(os.path.dirname(THIS_PATH), "../scripts"))
import numpy as np
import pyaudio3dtools
import pyivastest


def cmp_pcm(file1, file2, out_config, fs, get_mld=False, mld_lim=0, abs_tol=0) -> (int, str):
from .constants import ODG_PATTERN_PQEVALAUDIO


def cmp_pcm(
    ref_file,
    cmp_file,
    out_config,
    fs,
    get_mld=False,
    allow_differing_lengths=False,
    mld_lim=0,
    abs_tol=0,
    get_ssnr=False,
    get_odg=False,
) -> (int, str):
    """
    Compare 2 PCM files for bitexactness
    """
@@ -20,36 +37,52 @@ def cmp_pcm(file1, file2, out_config, fs, get_mld=False, mld_lim=0, abs_tol=0) -
    print("=====================")

    out_config = "MONO" if out_config == "" else out_config
    if out_config.upper() not in pyivastest.constants.OC_TO_NCHANNELS:
        out_config_in_file_names = os.path.splitext(os.path.basename(out_config))[0]
    # out_config may be a string or a Path. Wrap in str() to avoid error in case it is a Path.
    if str(out_config).upper() not in pyivastest.constants.OC_TO_NCHANNELS:
        nchannels = (
            pyivastest.IvasScriptsCommon.IvasScript.get_n_channels_from_ls_layout(
                out_config
            )
        )
    else:
        out_config_in_file_names = out_config
        nchannels = pyivastest.constants.OC_TO_NCHANNELS[out_config.upper()]

    s1, _ = pyaudio3dtools.audiofile.readfile(file1, nchannels, fs, outdtype=np.int16)
    s2, _ = pyaudio3dtools.audiofile.readfile(file2, nchannels, fs, outdtype=np.int16)
    s1, _ = pyaudio3dtools.audiofile.readfile(
        ref_file, nchannels, fs, outdtype=np.int16
    )
    s2, _ = pyaudio3dtools.audiofile.readfile(
        cmp_file, nchannels, fs, outdtype=np.int16
    )

    nchannels = s1.shape[
        1
    ]  # In case of wav input, override the nchannels with the one from the wav header
    # In case of wav input, override the nchannels with the one from the wav header
    nchannels = s1.shape[1]

    if s1.shape != s2.shape:
    if allow_differing_lengths:
        # to allow for MLD comparison, shorten longer file
        min_len = min(s1.shape[0], s2.shape[0])
        s1 = s1[:min_len, :]
        s2 = s2[:min_len, :]
    elif s1.shape != s2.shape:
        print(
            f"file size in samples: file 1 = {s1.shape[0]},",
            f"file 2 = {s2.shape[0]}",
        )
        return 1, "FAIL: File lengths differ"
        reason = "FAIL: File lengths differ. MAXIMUM ABS DIFF: None"
        if get_mld:
            reason += " - MLD: None"

        return 1, reason

    cmp_result = pyaudio3dtools.audioarray.compare(
        s1, s2, fs, per_frame=False, get_mld=get_mld
        s1,
        s2,
        fs,
        per_frame=False,
        get_mld=get_mld,
        get_ssnr=get_ssnr,
        ssnr_thresh_low=-50,
    )


    output_differs = 0
    reason = "SUCCESS: Files are bitexact"

@@ -75,13 +108,55 @@ def cmp_pcm(file1, file2, out_config, fs, get_mld=False, mld_lim=0, abs_tol=0) -
        else:
            reason += f" > {mld_lim}"

    if get_ssnr:
        reason += " - "
        for i, s in enumerate(cmp_result["SSNR"], start=1):
            msg = f"Channel {i} SSNR: {s}"
            reason += msg + " - "

    if get_odg:
        for n in range(nchannels):
            pqeval_output = pqevalaudio_wrapper(s1[:, n], s2[:, n], fs)

            match_odg = re.search(ODG_PATTERN_PQEVALAUDIO, pqeval_output)
            odg = float(match_odg.groups()[0])
            msg = f"Channel {n} ODG: {odg}"
            reason += " - " + msg
            print(msg)

    return output_differs, reason


def pqevalaudio_wrapper(
        ref_sig: np.ndarray,
        eval_sig: np.ndarray,
        fs: int,
        ) -> str:
    with tempfile.TemporaryDirectory() as tmp_dir:
        tmp_dir = Path(tmp_dir)
        tmp_file_ref = str(tmp_dir.joinpath("ref.wav"))
        tmp_file_eval = str(tmp_dir.joinpath("eval.wav"))

        # PQevalAudio neeeds 48 kHz sampling rate
        r48 = np.clip( pyaudio3dtools.audioarray.resample(ref_sig.astype(float), fs, 48000), -32768, 32767 ).astype(np.int16)
        t48 = np.clip( pyaudio3dtools.audioarray.resample(eval_sig.astype(float), fs, 48000), -32768, 32767 ).astype(np.int16)

        pyaudio3dtools.audiofile.writefile(tmp_file_ref, r48, 48000)
        pyaudio3dtools.audiofile.writefile(tmp_file_eval, t48, 48000)

        cmd = ["PQevalAudio", tmp_file_ref, tmp_file_eval]

        result = subprocess.run(cmd, capture_output=True)
        if result.returncode != 0:
            raise RuntimeError(f"Error running PQevalaudio: {result.stderr}")

        return result.stdout.decode("utf8")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("file1", type=str)
    parser.add_argument("file2", type=str)
    parser.add_argument("ref_file", type=str)
    parser.add_argument("cmp_file", type=str)
    parser.add_argument(
        "-o",
        "--out_config",
+157 −13
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ Pytest customization (configuration and fixtures) for the IVAS codec test suite.

import logging
import os
import re
from tests import testconfig
import pytest
import platform
@@ -41,6 +42,7 @@ import textwrap
from pathlib import Path
from subprocess import TimeoutExpired, run
from typing import Optional, Union
from .constants import MLD_PATTERN, MAX_DIFF_PATTERN, SSNR_PATTERN, ODG_PATTERN

logger = logging.getLogger(__name__)
USE_LOGGER_FOR_DBG = False  # current tests do not make use of the logger feature
@@ -128,6 +130,7 @@ def pytest_addoption(parser):
    parser.addoption(
        "--param_file",
        action="store",
        type=Path,
        help="If specified, use given param file in test_param_file.",
    )

@@ -170,6 +173,19 @@ def pytest_addoption(parser):
        help="MLD limit for comparison (default: 0)",
        default="0",
    )

    parser.addoption(
        "--ssnr",
        action="store_true",
        help="Compute Segmental SNR (SSNR) between ref and dut output instead of just comparing for bitexactness",
    )

    parser.addoption(
        "--odg",
        action="store_true",
        help="Get Objective Difference Grade for both conditions during comparison and report difference",
    )

    parser.addoption(
        "--create_ref",
        action="store_true",
@@ -182,6 +198,19 @@ def pytest_addoption(parser):
        default=False,
    )

    parser.addoption(
        "--use_ltv",
        action="store_true",
        default=False,
    )

    parser.addoption(
        "--ltv_dir",
        action="store",
        type=Path,
        default=None,
    )

    parser.addoption(
        "--dut_fr",
        help="Render frame size for the DUT output.",
@@ -241,6 +270,22 @@ def get_mld_lim(request):
    return float(request.config.getoption("--mld-lim"))


@pytest.fixture(scope="session", autouse=True)
def get_ssnr(request):
    """
    Return indication to compute ssnr during ref/dut comparison.
    """
    return request.config.option.ssnr


@pytest.fixture(scope="session", autouse=True)
def get_odg(request):
    """
    Return indication to compute ssnr during ref/dut comparison.
    """
    return request.config.option.odg


@pytest.fixture(scope="session")
def abs_tol(request) -> int:
    """
@@ -312,6 +357,7 @@ class EncoderFrontend:
        pca: Optional[bool] = None,
        quiet_mode: Optional[bool] = True,
        add_option_list: Optional[list] = None,
        run_dir: Optional[Path] = None,
    ) -> None:
        command = [self._path]

@@ -347,7 +393,11 @@ class EncoderFrontend:

        try:
            result = run(
                command, capture_output=True, check=False, timeout=self.timeout
                command,
                capture_output=True,
                check=False,
                timeout=self.timeout,
                cwd=run_dir,
            )
        except TimeoutExpired:
            pytest.fail(f"{self._type} encoder run timed out after {self.timeout}s.")
@@ -485,7 +535,9 @@ class DecoderFrontend:
        output_path: Path,
        quiet_mode: Optional[bool] = True,
        plc_file: Optional[Path] = None,
        netsim_profile: Optional[Path] = None,
        add_option_list: Optional[list] = None,
        run_dir: Optional[Path] = None,
    ) -> None:
        command = [self._path]

@@ -512,7 +564,7 @@ class DecoderFrontend:

            eid_command = [eid_path]
            eid_command.extend(["-fer", "-vbr", "-bs", "g192", "-ep", "g192"])
            eid_output_suffix = "." + os.path.basename(plc_file) + ".fer"
            eid_output_suffix = ".fer"
            eid_command += [
                str(input_bitstream_path),
                str(plc_file),
@@ -521,12 +573,53 @@ class DecoderFrontend:

            try:
                if not os.path.exists(str(input_bitstream_path) + eid_output_suffix):
                    result = run(eid_command, check=True)
            except Exception:
                pytest.fail("eid-xor operation failed!")
                    result = run(eid_command, check=True, cwd=run_dir)
            except Exception as e:
                pytest.fail(f"eid-xor operation failed! - {e}")

            input_bitstream_path += eid_output_suffix

        if netsim_profile is not None:
            system = platform.system()

            # TODO: centralize this in a utils file
            if system == "Windows":
                netsim_path = "./scripts/tools/Win32/networkSimulator_g192.exe"
            elif system == "Linux":
                netsim_path = "./scripts/tools/Linux/networkSimulator_g192"
            elif system == "Darwin":
                netsim_path = "./scripts/tools/Darwin/networkSimulator_g192"
            else:
                raise ValueError(f'Wrong system "{system}"!')

            if not os.path.isfile(netsim_path):
                raise FileNotFoundError(
                    f"network simulator binary {netsim_path} not found!\n"
                )
            netsim_bitstream_path = input_bitstream_path.with_suffix(".netsimout")
            tracefile_path = input_bitstream_path.with_suffix(".netsimtrace")
            # TODO: need to check if the "1" works with every profile
            netsim_command = [
                netsim_path,
                netsim_profile,
                input_bitstream_path,
                netsim_bitstream_path,
                tracefile_path,
                "1",
            ]
            print(netsim_command)
            try:
                run(netsim_command, check=True, cwd=run_dir)
            except Exception as e:
                pytest.fail(f"netsim operation failed! - {e}")

            input_bitstream_path = netsim_bitstream_path
            voip_opt = ["-voip"]
            if add_option_list is None:
                add_option_list = voip_opt
            elif "-voip" not in add_option_list:
                add_option_list.extend(voip_opt)

        if add_option_list is not None:
            command.extend(add_option_list)

@@ -545,7 +638,11 @@ class DecoderFrontend:

        try:
            result = run(
                command, capture_output=True, check=False, timeout=self.timeout
                command,
                capture_output=True,
                check=False,
                timeout=self.timeout,
                cwd=run_dir,
            )
        except TimeoutExpired:
            pytest.fail(f"{self._type} decoder run timed out after {self.timeout}s.")
@@ -727,15 +824,10 @@ def decoder_only(request) -> bool:

def pytest_configure(config):
    config.addinivalue_line("markers", "serial: mark test to run only in serial")
    config.addinivalue_line(
        "markers", "create_ref: mark test capable of producing references"
    )
    config.addinivalue_line(
        "markers",
        "create_ref_part2: reference creation test that depends on create_ref references",
    )
    if config.option.param_file:
        testconfig.PARAM_FILE = config.option.param_file
    if config.option.use_ltv:
        testconfig.use_ltv = True
    if config.option.selection_be_md5_file:
        md5_file_path = config.option.selection_be_md5_file
        if not platform.system() == "Windows":
@@ -746,3 +838,55 @@ def pytest_configure(config):
            testconfig.MD5_REF_DICT = {
                line.split()[0]: line.split()[1] for line in f.readlines()
            }


@pytest.fixture(scope="session")
def props_to_record(request, get_mld, get_ssnr, get_odg) -> str:
    props = ["MAXIMUM ABS DIFF"]
    if get_mld:
        props.append("MLD")
    if get_ssnr:
        props.append("SSNR")
    if get_odg:
        props.append("ODG")

    return props


def parse_properties(text_to_parse: str, output_differs: bool, props_to_record: list):
    """
    Record the given properties in the report by parsing their values from the text.
    """

    props = dict()

    for prop in props_to_record:
        if prop == "MLD":
            mld = float(re.search(MLD_PATTERN, text_to_parse).groups(1)[0])
            props[prop] = mld
        elif prop == "MAXIMUM ABS DIFF":
            max_diff = 0
            if output_differs:
                if (match := re.search(MAX_DIFF_PATTERN, text_to_parse)) is not None:
                    max_diff = match.groups(1)[0]
                else:
                    raise MaxDiffPatternNotFound()
            props[prop] = max_diff
        elif prop == "SSNR":
            ssnrs = re.findall(SSNR_PATTERN, text_to_parse)
            min_ssnr = min(ssnrs)
            min_ssnr_channel = ssnrs.index(min_ssnr)
            props["MIN_SSNR"] = min_ssnr
            props["MIN_SSNR_CHANNEL"] = min_ssnr_channel
        elif prop == "ODG":
            odgs = re.findall(ODG_PATTERN, text_to_parse)
            min_odg = min(odgs)
            min_odg_channel = odgs.index(min_odg)
            props["MIN_ODG"] = min_odg
            props["MIN_ODG_CHANNEL"] = min_odg_channel

    return props


class MaxDiffPatternNotFound(Exception):
    pass

tests/constants.py

0 → 100644
+12 −0
Original line number Diff line number Diff line
import pathlib

HERE = pathlib.Path(__file__).parent.absolute()
SCRIPTS_DIR = HERE.parent.joinpath("scripts")
TESTV_DIR = SCRIPTS_DIR.joinpath("testv")

# regex patterns for parsing the output from cmp_pcm -> mainly for BASOP ci
MLD_PATTERN = r"MLD: ([\d\.]*)"
MAX_DIFF_PATTERN = r"MAXIMUM ABS DIFF: (\d*)"
ODG_PATTERN_PQEVALAUDIO = r"Objective Difference Grade: (-*\d*\.\d*)"
ODG_PATTERN = r"ODG: (-*\d*\.\d*)"
SSNR_PATTERN = r"Channel \d* SSNR: (nan|[+-]*inf|[-*\d\.]*)"
Loading