Commit 13604332 authored by Jan Kiene's avatar Jan Kiene
Browse files

Merge branch 'ci/voip-be-test' into 'main'

[CI][BASOP-CI] extend jbm BE sanity test

See merge request !1626
parents 073ea9aa 899d33cf
Loading
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -1012,11 +1012,13 @@ voip-be-on-merge-request:
    - .test-job-linux-needs-testv-dir
    - .rules-merge-request
  stage: test
  needs: ["build-codec-linux-make", "codec-smoke-test"]
  needs: ["build-codec-linux-make"]
  timeout: "10 minutes"
  script:
    - *print-common-info
    - bash ci/ivas_voip_be_test.sh
    - make clean
    - make -j
    - python3 -m pytest tests/test_be_for_jbm_neutral_dly_profile.py

clang-format-check:
  extends:
@@ -2205,7 +2207,7 @@ coverage-test-on-main-scheduled:
    - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 -m create_ref --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec
    - bash ci/smoke_test.sh coverage
    - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 -m create_ref --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec
    - bash ci/ivas_voip_be_test.sh coverage
    - python3 -m pytest tests/test_be_for_jbm_neutral_dly_profile.py
    - lcov -c -d obj -o coverage.info
    # remove apps and lib_util files from coverage
    - lcov -r coverage.info "*apps*" -o coverage.info

ci/ivas_voip_be_test.sh

deleted100755 → 0
+0 −145
Original line number Diff line number Diff line
#! /usr/bin/bash

# (C) 2022-2024 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.

function usage {
    echo
    echo "Usage:"
    echo "      ivas_voip_be_test.sh [MODE]"
    echo 
    echo "      MODE - test (default) or coverage"
    exit
}

if [ ! -d "lib_com" ]; then
    echo "not in root directory! - please run in IVAS root"
    exit 1
fi

if [ -z "$1" ] || [ "$1" == "test" ]; then
    WORKERS=""
    BUILD=1
    COVERAGE=0
elif [ "$1" == "coverage" ]; then
    WORKERS="-t 1"
    BUILD=0
    COVERAGE=1
else
    usage
fi

if [ $BUILD -eq 1 ];then
    make clean
    make all -j
fi

# Configuration
modes=('HOA3_b128_wb_cbr' 'MC_7_1_b96_fb_cbr' 'ISM2_b48_fb_cbr')
output_formats=('STEREO' 'FOA' '7_1' 'HOA3')
limit_input_to_x_seconds=30
verbosity_cmd="-z console"

cfg=./scripts/config/ci_linux.json
dly_profile=./scripts/dly_error_profiles/dly_error_profile_0.dat

output_dir_default="out"
output_dir_voip="out_voip"

# Run the same modes in VoIP and non-VoIP mode with a neutral delay profile
./scripts/runIvasCodec.py $verbosity_cmd -p $cfg $WORKERS -U $limit_input_to_x_seconds -m "${modes[@]}" --oc "${output_formats[@]}" -o $output_dir_default | tee voip_be_test_output.txt
./scripts/runIvasCodec.py $verbosity_cmd -p $cfg $WORKERS -U $limit_input_to_x_seconds -m "${modes[@]}" --oc "${output_formats[@]}" -o $output_dir_voip -J "$dly_profile" | tee -a voip_be_test_output.txt

# Check if Python scripts above failed. They return status 0 even when running a mode fails, so we have to parse log file
if grep -iq failed voip_be_test_output.txt ; then
    echo "Run errors in runIvasCodec.py"
    exit 1
fi

if [ $COVERAGE -eq 1 ];then
    # Coverage analysis requires only running the codec and may exit before the comparison part
    exit 0
fi

# Set up Python path
python_audio_module_path=$(pwd)/scripts
export PYTHONPATH=$python_audio_module_path:$PYTHONPATH
python_audiofile_script_path=$python_audio_module_path/pyaudio3dtools/audiofile.py

# Trim JBM delay from VoIP output files
output_dir_voip_dec="$output_dir_voip"/dec
output_dir_voip_dec_trimmed="$output_dir_voip"/dec_trimmed

if [[ ! -d $output_dir_voip_dec_trimmed ]]; then
    mkdir $output_dir_voip_dec_trimmed 
fi

for cut in "$output_dir_voip_dec"/*.wav; do
    output_path=${cut/$output_dir_voip_dec/$output_dir_voip_dec_trimmed}
    output_path=${output_path/".wav"/".raw"}
    python3 "$python_audiofile_script_path" pre-trim 60 "$cut" "$output_path" | tee -a voip_be_test_output.txt
done

# Convert non-VoIP output from wav to pcm (comparison script doesn't support wav)
output_dir_default_dec="$output_dir_default"/dec
output_dir_default_dec_pcm="$output_dir_default"/dec_pcm

if [[ ! -d $output_dir_default_dec_pcm ]]; then
    mkdir $output_dir_default_dec_pcm 
fi

for ref in "$output_dir_default_dec"/*.wav; do
    output_path=${ref/$output_dir_default_dec/$output_dir_default_dec_pcm}
    output_path=${output_path/".wav"/".raw"}
    python3 "$python_audiofile_script_path" convert "$ref" "$output_path" | tee -a voip_be_test_output.txt
done

# Assert BE between non-VoIP and VoIP modes
all_be=1

cmp_tool_path=$(pwd)/tests/cmp_pcm.py

for ref in "$output_dir_default_dec_pcm"/*; do
    cut=${ref/$output_dir_default_dec_pcm/$output_dir_voip_dec_trimmed}
    cut=${cut/".dec."/"_jbm_dly_error_profile_0_dat.dec."}

    # Print paths of compared files, since the script doesn't do it
    printf "\nComparing %s and %s\n" "$ref" "$cut" | tee -a voip_be_test_output.txt
    printout=$($cmp_tool_path "$ref" "$cut")
    if [ $? -ne 0 ]; then
        all_be=0
    fi
    printf "%s\n" "$printout" | tee -a voip_be_test_output.txt
done

if [ $all_be -eq 1 ]; then
    printf "\n\nAll tested conditions are bit-exact\n" | tee -a voip_be_test_output.txt
else
    printf "\n\nBitexactness problems found!\n" | tee -a voip_be_test_output.txt
    exit 1;
fi
+26 −7
Original line number Diff line number Diff line
@@ -49,16 +49,21 @@ SEG_SNR_EXPR = r"Seg. SNR\s+=(.+)dB"
DIFF_EXPR = r"Max Diff\s+=\s+(\d+)"
DIFF_STR = {
    "CompAudio": "{label} Max. diff (PCM) for file {f}: {diff}",
    "pyaudio3dtools": "{label} Max. diff (PCM) for file {f}: {diff}",
    "mld": "{label} MLD diff for file {f}: {diff}",
}


def main(args):
    tool = args.tool
    if shutil.which(tool) is None:
    if tool != "pyaudio3dtools" and shutil.which(tool) is None:
        print(f"{tool} not in PATH - abort.")
        sys.exit(-1)

    test_offset_ms = args.test_offset_ms
    if tool != "pyaudio3dtools":
        test_offset_ms = 0

    num_files_diff = 0

    with OutFileManager(args.out_file) as out_file:
@@ -77,7 +82,7 @@ def main(args):
            # if only one thread is passed, do everything in the main thread
            # to allow for meaningful debugging if needed
            for f in common_files:
                compare_files(f, fol1, fol2, outputs, tool)
                compare_files(f, fol1, fol2, outputs, tool, test_offset_ms)
        else:
            with concurrent.futures.ThreadPoolExecutor(
                max_workers=args.num_threads
@@ -89,6 +94,7 @@ def main(args):
                    repeat(fol2),
                    repeat(outputs),
                    repeat(tool),
                    repeat(test_offset_ms)
                )

        if args.sort:
@@ -98,7 +104,7 @@ def main(args):

        # write csv header
        if out_file is not None:
            if tool == "CompAudio":
            if tool == "CompAudio" or tool == "pyaudio3dtools":
                out_file.write("filename,diff\n")
            elif tool == "mld":
                out_file.write("filename,mld\n")
@@ -106,7 +112,7 @@ def main(args):
        for f, tool_output in out.items():
            if tool == "CompAudio":
                diff, snr, gain, seg_snr = tool_output
            elif tool == "mld":
            elif tool == "mld" or tool == "pyaudio3dtools":
                diff = tool_output

            if diff > 0:
@@ -133,8 +139,10 @@ def main(args):
        else:
            print("All files are bitexact")

    return num_files_diff


def compare_files(f, fol1, fol2, outputs_dict, tool):
def compare_files(f, fol1, fol2, outputs_dict, tool, test_offset_ms):
    """
    Compare file f in both folders fol1 and fol2 using the given tool and
    store the parsed difference in outputs_dict.
@@ -155,6 +163,11 @@ def compare_files(f, fol1, fol2, outputs_dict, tool):
        s2, fs2 = readfile(f2, outdtype="int16")
        cmp_result = compare(s1, s2, fs1, per_frame=False, get_mld=True)
        tool_output = cmp_result["MLD"]
    elif tool == "pyaudio3dtools":
        s1, fs1 = readfile(f1, outdtype="int16")
        s2, fs2 = readfile(f2, outdtype="int16")
        cmp_result = compare(s1, s2, fs1, per_frame=False, test_start_offset_ms=test_offset_ms)
        tool_output = cmp_result["max_abs_diff"]

    with threading.Lock():
        outputs_dict.update({f: tool_output})
@@ -284,10 +297,16 @@ if __name__ == "__main__":
    )
    parser.add_argument(
        "--tool",
        choices=["mld", "CompAudio"],
        choices=["mld", "CompAudio", "pyaudio3dtools"],
        default="CompAudio",
        help="Compare tool to run",
    )
    parser.add_argument(
            "--test_offset_ms",
            type=int,
            default=0,
            help="Offset in miliseconds that is ignored at the start of the files in folder2 (only used if tool=pyaudio3dtools)"
            )
    args = parser.parse_args()

    main(args)
    sys.exit(main(args))
+12 −0
Original line number Diff line number Diff line
@@ -237,6 +237,7 @@ def compare(
    ssnr_thresh_low: float = -np.inf,
    ssnr_thresh_high: float = np.inf,
    apply_thresholds_to_ref_only: bool = False,
    test_start_offset_ms: int = 0,
) -> dict:
    """Compare two audio arrays

@@ -264,12 +265,23 @@ def compare(
        Set to True to only apply the threshold comparison for the reference signal
        for whether to include a segment in the ssnr computation. Use this to align
        behaviour with the MPEG-D conformance specification.
    test_start_offset_ms: (non-negative) int
        offset in miliseconds for test signal. If > 0, the corresponding number of samples
        will be removed from the test array like so: test = test[sample_offset:, :].

    Returns
    -------
    result: dict
        Comparison results
    """

    if test_start_offset_ms < 0:
        raise ValueError(
            f"Test_start_offset_ms has to be non-negative, but {test_start_offset_ms} was given."
        )
    test_start_offset_samples = int(fs * test_start_offset_ms / 1000)
    test = test[test_start_offset_samples:, :]

    framesize = fs // 50
    diff = abs(test - ref)
    max_diff = int(diff.max())
+20 −0
Original line number Diff line number Diff line
@@ -780,5 +780,25 @@ if __name__ == "__main__":
    parser_convert.add_argument("output_file")
    parser_convert.set_defaults(func=convert_wrapper)

    def compare_wrapper(compare_args):
        if not compare_args.ref_file.endswith(".wav") or not compare_args.test_file.endswith(".wav"):
            print("Convert currently only supported with WAV file input")
            exit(-1)

        s1, fs1 = readfile(compare_args.ref_file, outdtype="int16")
        s2, fs2 = readfile(compare_args.test_file, outdtype="int16")
        if fs1 != fs2:
            print("Can only compare signals of same sampling rate")
            exit(-1)

        cmp_result = audioarray.compare(s1, s2, fs1, per_frame=False, test_start_offset_ms=compare_args.test_start_offset_ms)
        exit(cmp_result["max_abs_diff"])

    parser_compare = subparsers.add_parser("compare", help="Compare two wav files for bitexactness")
    parser_compare.add_argument("ref_file")
    parser_compare.add_argument("test_file")
    parser_compare.add_argument("test_start_offset_ms", default=0, type=int)
    parser_compare.set_defaults(func=compare_wrapper)

    args = parser.parse_args()
    args.func(args)
Loading