From 14bd218d801e2db797bbf6ce1aa02b9845a8a617 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Tue, 11 Apr 2023 18:30:56 +0200 Subject: [PATCH 01/31] started working on scaling after splitting --- README.md | 2 +- examples/TEMPLATE.yml | 2 +- ivas_processing_scripts/__init__.py | 6 +++- .../audiotools/audiofile.py | 5 +++- .../binaural_datasets/binaural_dataset.py | 3 +- .../audiotools/wrappers/bs1770.py | 28 ++++++++++++++++--- .../processing/processing.py | 7 ++++- 7 files changed, 43 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4669de76..3449c860 100755 --- a/README.md +++ b/README.md @@ -376,7 +376,7 @@ postprocessing: ### default = null (uses postprocessing fmt if possible) # loudness_fmt: null ### Name of custom binaural dataset (without prefix or suffix); - ### default = null (ORANGE53_Dolby for BINAURAL, IISofficialMPEG222UC for BINAURAL_ROOM) + ### default = null (ORANGE53(_Dolby) for BINAURAL, IISofficialMPEG222UC for BINAURAL_ROOM) # bin_dataset: SADIE ### Render LFE to binaural output with the specified gain (only valid for channel-based input); default = null # bin_lfe_gain: 1 diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 030bd437..c63114e3 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -234,7 +234,7 @@ postprocessing: ### default = null (uses postprocessing fmt if possible) # loudness_fmt: null ### Name of custom binaural dataset (without prefix or suffix); - ### default = null (ORANGE53_Dolby for BINAURAL, IISofficialMPEG222UC for BINAURAL_ROOM) + ### default = null (ORANGE53(_Dolby) for BINAURAL, IISofficialMPEG222UC for BINAURAL_ROOM) # bin_dataset: SADIE ### Render LFE to binaural output with the specified gain (only valid for channel-based input); default = null # bin_lfe_gain: 1 diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index 6d48d5d5..1480da93 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -49,6 +49,7 @@ from ivas_processing_scripts.processing.processing import ( process_item, ) from ivas_processing_scripts.utils import DirManager, apply_func_parallel +from ivas_processing_scripts.audiotools.wrappers.bs1770 import scale_files def logging_init(args, cfg): @@ -145,7 +146,10 @@ def main(args): if cfg.concatenate_input: # write out the splits, optionally remove file - concat_teardown(cfg, logger) + out_paths_splits, out_meta_splits = concat_teardown(cfg, logger) + # scale individual files + if cfg.postprocessing.get("loudness", False): + scale_files(out_paths_splits, cfg.postprocessing["fmt"], cfg.postprocessing["loudness"], out_meta_splits) # copy configuration to output directory with open(cfg.output_path.joinpath(f"{cfg.name}.yml"), "w") as f: diff --git a/ivas_processing_scripts/audiotools/audiofile.py b/ivas_processing_scripts/audiotools/audiofile.py index 3b127f3b..d08559d7 100755 --- a/ivas_processing_scripts/audiotools/audiofile.py +++ b/ivas_processing_scripts/audiotools/audiofile.py @@ -221,9 +221,10 @@ def split( splits: list[int], in_fs: Optional[int] = 48000, preamble: Optional[int] = 0, + loudness: Optional[float] = None, ) -> list[Union[str, Path]]: """ - Horizontally splits audio files into multiple shorter files and applied windowing + Horizontally splits audio files into multiple shorter files and applies windowing and scaling Parameters __________ @@ -237,6 +238,8 @@ def split( List of sample indices where to cut the signal in_fs: Optional[int] Input sampling rate, default 48000 Hz + loudness: Optional[float] + Desired loudness of individual files """ # create a list of output files diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py index 76f44f30..7062dfb3 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py +++ b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py @@ -129,7 +129,7 @@ def load_ir( elif out_fmt.startswith("BINAURAL"): dataset_prefix = "HRIR" if dataset is None: - dataset = "ORANGE53_Dolby" + dataset = "ORANGE53" if in_fmt in OBJECT_BASED_AUDIO_FORMATS.keys() or in_fmt.startswith( "CUSTOM_LS" @@ -138,6 +138,7 @@ def load_ir( elif in_fmt in CHANNEL_BASED_AUDIO_FORMATS.keys() and in_fmt != "MONO": dataset_suffix = "LS" elif in_fmt in SCENE_BASED_AUDIO_FORMATS.keys(): + dataset = "ORANGE53_Dolby" if in_fmt == "SBA1" or in_fmt == "FOA": dataset_suffix = "SBA1" # Use truncated SBA3 dataset if no SBA1 or 2 dataset exists diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index 041385f8..a8193ca0 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -33,7 +33,7 @@ import logging from pathlib import Path from tempfile import TemporaryDirectory -from typing import Optional, Tuple +from typing import Optional, Tuple, Union from warnings import warn import numpy as np @@ -188,7 +188,7 @@ def loudness_norm( input: audio.Audio, target_loudness: Optional[float] = -26, loudness_format: Optional[str] = None, -) -> Tuple[np.ndarray, float]: +) -> np.ndarray: """ Iterative loudness normalization using ITU-R BS.1770-4 Signal is iteratively scaled after rendering to the specified format @@ -207,8 +207,6 @@ def loudness_norm( ------- norm : Audio Normalized audio - scale_factor: float - Effectively applied scale factor """ # repeat until convergence of loudness @@ -235,3 +233,25 @@ def loudness_norm( ) return input.audio + + +def scale_files( + file_list: list[Union[Path, str]], + out_fmt: str, + loudness: float, + fs: Optional[int] = 48000, + in_meta: Optional[list] = None, +): + if in_meta is None: + np.repeat(None) + + for file, meta in zip(file_list, in_meta): + + # create audio object + audio_obj = audio.fromfile(out_fmt, file, fs, meta) # TODO: handle metadata + + # adjust loudness + scaled_audio = loudness_norm(audio_obj, loudness, out_fmt) + + # write into file + write(file, scaled_audio, audio_obj.fs) diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 30fb3068..d1012f98 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -140,6 +140,7 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): logger.info( f"No splitting of output file necessary since only one signal used." ) + out_meta = None else: logger.info(f"Splitting output file in directory {cfg.output_path}") @@ -156,14 +157,18 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): if output_format.startswith("ISM"): for odir in cfg.out_dirs: path_input = odir / cfg.items_list[0].name - split_meta_in_file( + out_meta = split_meta_in_file( path_input, odir, cfg.split_names, cfg.splits, output_format, preamble=cfg.preamble ) + else: + out_meta = None # remove concatenated file if cfg.delete_tmp: cfg.concat_file.unlink(missing_ok=True) + return out_paths, out_meta # TODO: append paths for each odir + def preprocess(cfg, in_meta, logger): preprocessing = cfg.proc_chains[0] -- GitLab From dd4ca712ae78e423c706fdb12cb96ebb29630be9 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 12 Apr 2023 17:33:16 +0200 Subject: [PATCH 02/31] added loudness adjustment after splitting --- ivas_processing_scripts/__init__.py | 3 +- .../audiotools/metadata.py | 5 +- .../audiotools/wrappers/bs1770.py | 51 ++++++++++++++----- .../processing/processing.py | 11 ++-- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index 1480da93..be81854e 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -149,7 +149,8 @@ def main(args): out_paths_splits, out_meta_splits = concat_teardown(cfg, logger) # scale individual files if cfg.postprocessing.get("loudness", False): - scale_files(out_paths_splits, cfg.postprocessing["fmt"], cfg.postprocessing["loudness"], out_meta_splits) + # TODO: take care of samplingrate + scale_files(out_paths_splits, cfg.postprocessing["fmt"], cfg.postprocessing["loudness"], cfg.postprocessing.get("fs", None), out_meta_splits) # copy configuration to output directory with open(cfg.output_path.joinpath(f"{cfg.name}.yml"), "w") as f: diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index a3f41bac..820af6fa 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -400,6 +400,7 @@ def split_meta_in_file( split_old = 0 for idx, split in enumerate(splits): + out_paths_obj = [] for obj in range(audio_object.num_channels): out_file = ( Path(out_folder) @@ -407,7 +408,7 @@ def split_meta_in_file( ) # add the path to our list - out_paths.append(out_file) + out_paths_obj.append(out_file) # remove preamble if preamble: @@ -424,6 +425,8 @@ def split_meta_in_file( # write file write_ISM_metadata_in_file([y], [out_file]) + out_paths.append(out_paths_obj) + split_old = split return out_paths diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index a8193ca0..e47bfa4a 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 - +import copy # # (C) 2022-2023 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., @@ -236,22 +236,47 @@ def loudness_norm( def scale_files( - file_list: list[Union[Path, str]], - out_fmt: str, + file_list: list[list[Union[Path, str]]], + fmt: str, loudness: float, fs: Optional[int] = 48000, in_meta: Optional[list] = None, -): - if in_meta is None: - np.repeat(None) +) -> None: + """ + Scales audio files to desired loudness + + Parameters + ---------- + file_list : list[list[Union[Path, str]]] + List of file paths in a list of the condition folders + fmt: str + Audio format of files in list + loudness: float + Desired loudness level in LKFS/dBov + fs: Optional[int] + Sampling rate + in_meta: Optional[list] + Metadata for ISM with same structure as file_list but one layer more + for the list of metadata for one file + """ + + if fmt.startswith("ISM") and in_meta: + meta_bool = True + else: + in_meta = copy.copy(file_list) + meta_bool = False - for file, meta in zip(file_list, in_meta): + for folder, meta_folder in zip(file_list, in_meta): + for file, meta in zip(folder, meta_folder): - # create audio object - audio_obj = audio.fromfile(out_fmt, file, fs, meta) # TODO: handle metadata + # create audio object + if meta_bool: + audio_obj = audio.fromfile(fmt, file, fs, meta) + else: + audio_obj = audio.fromfile(fmt, file, fs) - # adjust loudness - scaled_audio = loudness_norm(audio_obj, loudness, out_fmt) + # adjust loudness + scaled_audio = loudness_norm(audio_obj, loudness) - # write into file - write(file, scaled_audio, audio_obj.fs) + # write into file + write(file, scaled_audio, audio_obj.fs) diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index d1012f98..1680cde0 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -136,6 +136,9 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): output_format = cfg.postprocessing["fmt"] + out_files = [] + out_meta = [] + if num_splits <= 1: logger.info( f"No splitting of output file necessary since only one signal used." @@ -152,22 +155,22 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): logger.debug( f"Resulting split files condition {odir.name}: {', '.join([str(op) for op in out_paths])}" ) + out_files.append(out_paths) # split ISM metadata if output_format.startswith("ISM"): for odir in cfg.out_dirs: path_input = odir / cfg.items_list[0].name - out_meta = split_meta_in_file( + out_meta_paths = split_meta_in_file( path_input, odir, cfg.split_names, cfg.splits, output_format, preamble=cfg.preamble ) - else: - out_meta = None + out_meta.append(out_meta_paths) # remove concatenated file if cfg.delete_tmp: cfg.concat_file.unlink(missing_ok=True) - return out_paths, out_meta # TODO: append paths for each odir + return out_files, out_meta def preprocess(cfg, in_meta, logger): -- GitLab From 1babf7f9ef8746403e4482aaa4cb60d20df29394 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Thu, 13 Apr 2023 10:59:19 +0200 Subject: [PATCH 03/31] formatting --- ivas_processing_scripts/__init__.py | 10 ++++++-- .../audiotools/audioarray.py | 16 +++++++----- .../binaural_datasets/binaural_dataset.py | 17 +++++++++---- .../audiotools/convert/__init__.py | 14 ++++++----- .../audiotools/metadata.py | 13 +++++++--- .../audiotools/wrappers/bs1770.py | 25 ++++++++++--------- .../audiotools/wrappers/esdru.py | 2 +- .../audiotools/wrappers/p50fbmnru.py | 4 +-- ivas_processing_scripts/constants.py | 11 +++++++- ivas_processing_scripts/processing/chains.py | 5 ++-- ivas_processing_scripts/processing/config.py | 4 +-- ivas_processing_scripts/processing/evs.py | 10 +++++--- ivas_processing_scripts/processing/ivas.py | 10 +++++--- .../processing/processing.py | 15 +++++++---- 14 files changed, 102 insertions(+), 54 deletions(-) diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index be81854e..e51fdf34 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -36,6 +36,7 @@ from itertools import repeat import yaml from ivas_processing_scripts.audiotools.metadata import check_ISM_metadata +from ivas_processing_scripts.audiotools.wrappers.bs1770 import scale_files from ivas_processing_scripts.constants import ( LOGGER_DATEFMT, LOGGER_FORMAT, @@ -49,7 +50,6 @@ from ivas_processing_scripts.processing.processing import ( process_item, ) from ivas_processing_scripts.utils import DirManager, apply_func_parallel -from ivas_processing_scripts.audiotools.wrappers.bs1770 import scale_files def logging_init(args, cfg): @@ -150,7 +150,13 @@ def main(args): # scale individual files if cfg.postprocessing.get("loudness", False): # TODO: take care of samplingrate - scale_files(out_paths_splits, cfg.postprocessing["fmt"], cfg.postprocessing["loudness"], cfg.postprocessing.get("fs", None), out_meta_splits) + scale_files( + out_paths_splits, + cfg.postprocessing["fmt"], + cfg.postprocessing["loudness"], + cfg.postprocessing.get("fs", None), + out_meta_splits, + ) # copy configuration to output directory with open(cfg.output_path.joinpath(f"{cfg.name}.yml"), "w") as f: diff --git a/ivas_processing_scripts/audiotools/audioarray.py b/ivas_processing_scripts/audiotools/audioarray.py index 2f921b3f..0cc88f77 100755 --- a/ivas_processing_scripts/audiotools/audioarray.py +++ b/ivas_processing_scripts/audiotools/audioarray.py @@ -42,7 +42,9 @@ logger = logging.getLogger("__main__") logger.setLevel(logging.DEBUG) -### Functions used in this module +"""Functions used in this module""" + + def trim( x: np.ndarray, fs: Optional[int] = 48000, @@ -183,13 +185,13 @@ def delay_compensation( """ # Get the delay in number of samples - if flt_type == "SHQ2" and up == True: + if flt_type == "SHQ2" and up: d_samples = DELAY_COMPENSATION_FOR_FILTERING["SHQ2"]["up"] - elif flt_type == "SHQ2" and down == True: + elif flt_type == "SHQ2" and down: d_samples = DELAY_COMPENSATION_FOR_FILTERING["SHQ2"]["down"] - elif flt_type == "SHQ3" and up == True: + elif flt_type == "SHQ3" and up: d_samples = DELAY_COMPENSATION_FOR_FILTERING["SHQ3"]["up"] - elif flt_type == "SHQ3" and down == True: + elif flt_type == "SHQ3" and down: d_samples = DELAY_COMPENSATION_FOR_FILTERING["SHQ3"]["down"] else: d_samples = DELAY_COMPENSATION_FOR_FILTERING[flt_type] @@ -405,7 +407,9 @@ def framewise_io( ) -### Deprecated functions (partly replaced by ITU binaries) +"""Deprecated functions (partly replaced by ITU binaries)""" + + def resample( x: np.ndarray, in_freq: int, diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py index 7062dfb3..ab86d66a 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py +++ b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py @@ -30,7 +30,6 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # -import logging import warnings from pathlib import Path from typing import Optional, Tuple, Union @@ -142,15 +141,23 @@ def load_ir( if in_fmt == "SBA1" or in_fmt == "FOA": dataset_suffix = "SBA1" # Use truncated SBA3 dataset if no SBA1 or 2 dataset exists - if not (Path(__file__).parent.joinpath(f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat")).is_file(): + if not ( + Path(__file__).parent.joinpath( + f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat" + ) + ).is_file(): dataset_suffix = "SBA3" - warnings.warn(f"No SBA1 dataset found -> use truncated SBA3 dataset") + warnings.warn("No SBA1 dataset found -> use truncated SBA3 dataset") elif in_fmt.endswith("2"): dataset_suffix = "SBA2" # Use truncated SBA3 dataset if no SBA1 or 2 dataset exists - if not (Path(__file__).parent.joinpath(f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat")).is_file(): + if not ( + Path(__file__).parent.joinpath( + f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat" + ) + ).is_file(): dataset_suffix = "SBA3" - warnings.warn(f"No SBA2 dataset found -> use truncated SBA3 dataset") + warnings.warn("No SBA2 dataset found -> use truncated SBA3 dataset") else: dataset_suffix = "SBA3" diff --git a/ivas_processing_scripts/audiotools/convert/__init__.py b/ivas_processing_scripts/audiotools/convert/__init__.py index 9b09cf8f..d92c2b3a 100755 --- a/ivas_processing_scripts/audiotools/convert/__init__.py +++ b/ivas_processing_scripts/audiotools/convert/__init__.py @@ -41,12 +41,12 @@ from ivas_processing_scripts.audiotools.convert.masa import convert_masa from ivas_processing_scripts.audiotools.convert.objectbased import convert_objectbased from ivas_processing_scripts.audiotools.convert.scenebased import convert_scenebased from ivas_processing_scripts.audiotools.wrappers.bs1770 import loudness_norm +from ivas_processing_scripts.audiotools.wrappers.esdru import esdru from ivas_processing_scripts.audiotools.wrappers.filter import ( hp50filter_itu, lpfilter_itu, resample_itu, ) -from ivas_processing_scripts.audiotools.wrappers.esdru import esdru from ivas_processing_scripts.audiotools.wrappers.p50fbmnru import p50fbmnru from ..metadata import write_ISM_metadata_in_file @@ -258,13 +258,13 @@ def process_audio( """MNRU""" if mnru_q is not None: if logger: - logger.debug(f"Applying P.50 Fullband MNRU") + logger.debug("Applying P.50 Fullband MNRU") x.audio = p50fbmnru(x, mnru_q) """ESDRU""" if esdru_alpha is not None: if logger: - logger.debug(f"Applying ESDRU Recommendation ITU-T P.811") + logger.debug("Applying ESDRU Recommendation ITU-T P.811") x.audio = esdru(x, esdru_alpha) """limiting""" @@ -284,17 +284,19 @@ def format_conversion( # validation if isinstance(output, audio.MetadataAssistedSpatialAudio): - raise NotImplementedError(f"MASA is not supported as an output for rendering!") + raise NotImplementedError("MASA is not supported as an output for rendering!") if isinstance(output, audio.ObjectBasedAudio) and input.name != output.name: raise NotImplementedError( - f"ISM is not supported as an output for rendering! Only usable as pass-through" + "ISM is not supported as an output for rendering! Only usable as pass-through" ) if logger: logger.debug(f"Format conversion: {input.name} -> {output.name}") - if input.name == output.name or (input.name.startswith("BINAURAL") and output.name.startswith("BINAURAL")): + if input.name == output.name or ( + input.name.startswith("BINAURAL") and output.name.startswith("BINAURAL") + ): output.audio = input.audio else: if isinstance(input, audio.BinauralAudio): diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index 820af6fa..00cf8601 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -413,7 +413,12 @@ def split_meta_in_file( # remove preamble if preamble: preamble_frames = int(preamble / IVAS_FRAME_LEN_MS) - y = trim(audio_object.object_pos[obj], audio_object.fs, (preamble_frames, 0), samples=True) + y = trim( + audio_object.object_pos[obj], + audio_object.fs, + (preamble_frames, 0), + samples=True, + ) else: y = audio_object.object_pos[obj] @@ -448,7 +453,7 @@ def check_ISM_metadata( path_meta = in_meta["all_items"] except KeyError: raise ValueError( - f'Only one metadata path is given but not with key "all_items".' + 'Only one metadata path is given but not with key "all_items".' ) list_meta = metadata_search(path_meta, item_names, num_objects) @@ -475,7 +480,7 @@ def check_ISM_metadata( # just read out list_item = current_item else: - raise ValueError(f"Number of objects and metadata does not match.") + raise ValueError("Number of objects and metadata does not match.") list_meta.append(list_item) else: raise ValueError("Number of metadata inputs does not match number of items") @@ -492,7 +497,7 @@ def metadata_search( """Search for ISM metadata with structure item_name.{0-3}.csv in in_meta folder""" if not item_names: - raise ValueError(f"Item names not provided, can't search for metadata") + raise ValueError("Item names not provided, can't search for metadata") list_meta = [] for item in item_names: diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index e47bfa4a..d7c8739a 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -1,5 +1,18 @@ #!/usr/bin/env python3 import copy +import logging +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Optional, Tuple, Union +from warnings import warn + +import numpy as np + +from ivas_processing_scripts.audiotools import audio, convert +from ivas_processing_scripts.audiotools.audiofile import write +from ivas_processing_scripts.audiotools.wrappers.filter import resample_itu +from ivas_processing_scripts.utils import find_binary, get_devnull, run + # # (C) 2022-2023 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., @@ -30,18 +43,6 @@ import copy # the United Nations Convention on Contracts on the International Sales of Goods. # -import logging -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import Optional, Tuple, Union -from warnings import warn - -import numpy as np - -from ivas_processing_scripts.audiotools import audio, convert -from ivas_processing_scripts.audiotools.audiofile import write -from ivas_processing_scripts.audiotools.wrappers.filter import resample_itu -from ivas_processing_scripts.utils import find_binary, get_devnull, run logger = logging.getLogger("__main__") logger.setLevel(logging.DEBUG) diff --git a/ivas_processing_scripts/audiotools/wrappers/esdru.py b/ivas_processing_scripts/audiotools/wrappers/esdru.py index 75757961..a26ff511 100755 --- a/ivas_processing_scripts/audiotools/wrappers/esdru.py +++ b/ivas_processing_scripts/audiotools/wrappers/esdru.py @@ -115,7 +115,7 @@ def esdru( write(tmp_output_file, tmp_output_signal, sf) # run command - result = run(cmd) + run(cmd) tmp_output_signal, out_fs = read(tmp_output_file, 2, sf) diff --git a/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py b/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py index a6b80187..0c67b679 100755 --- a/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py +++ b/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py @@ -32,7 +32,7 @@ from pathlib import Path from tempfile import TemporaryDirectory -from typing import Optional, Tuple +from typing import Tuple from warnings import warn import numpy as np @@ -97,7 +97,7 @@ def p50fbmnru( write(tmp_output_file, tmp_output_signal) # run command - result = run(cmd) + run(cmd) tmp_output_signal, out_fs = read(tmp_output_file, input.num_channels) diff --git a/ivas_processing_scripts/constants.py b/ivas_processing_scripts/constants.py index 3c518494..ff56b9bf 100755 --- a/ivas_processing_scripts/constants.py +++ b/ivas_processing_scripts/constants.py @@ -40,7 +40,16 @@ LOGGER_FORMAT = ( ) LOGGER_DATEFMT = "%m-%d %H:%M:%S" -SUPPORTED_CONDITIONS = {"ref", "lp3k5", "lp7k", "mnru", "esdru", "evs", "ivas", "mono_dmx"} +SUPPORTED_CONDITIONS = { + "ref", + "lp3k5", + "lp7k", + "mnru", + "esdru", + "evs", + "ivas", + "mono_dmx", +} DEFAULT_CONFIG = { # general options diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index e495f531..f9edbc36 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -181,7 +181,8 @@ def get_processing_chain( "mnru_q": tmp_mnru_q, "esdru_alpha": tmp_esdru_alpha, } - )) + ) + ) tmp_in_fmt = "MONO" # add another postprocessing from in_fmt to mono elif cond_cfg["type"] == "evs": @@ -210,7 +211,7 @@ def get_processing_chain( cod_cfg = cond_cfg["cod"] dec_cfg = cond_cfg["dec"] - ### local tx overrides global one, or just allow global? + # TODO: local tx overrides global one, or just allow global? if "tx" in cond_cfg.keys(): tx_cfg = cond_cfg["tx"] elif hasattr(cfg, "tx"): diff --git a/ivas_processing_scripts/processing/config.py b/ivas_processing_scripts/processing/config.py index a6a943bd..b281e2a3 100755 --- a/ivas_processing_scripts/processing/config.py +++ b/ivas_processing_scripts/processing/config.py @@ -40,10 +40,10 @@ from ivas_processing_scripts.constants import ( DEFAULT_CONFIG_EVS, DEFAULT_CONFIG_IVAS, REQUIRED_KEYS, + REQUIRED_KEYS_ESDRU, REQUIRED_KEYS_EVS, REQUIRED_KEYS_IVAS, REQUIRED_KEYS_MNRU, - REQUIRED_KEYS_ESDRU, SUPPORTED_CONDITIONS, ) @@ -174,7 +174,7 @@ class TestConfig: ) elif type == "esdru": if REQUIRED_KEYS_ESDRU.difference( - cfg["conditions_to_generate"][cond_name].keys() + cfg["conditions_to_generate"][cond_name].keys() ): raise KeyError( f"The following key must be specified for ESDRU: {REQUIRED_KEYS_ESDRU}" diff --git a/ivas_processing_scripts/processing/evs.py b/ivas_processing_scripts/processing/evs.py index 0e9c3e44..98cb5ce8 100755 --- a/ivas_processing_scripts/processing/evs.py +++ b/ivas_processing_scripts/processing/evs.py @@ -31,11 +31,11 @@ # import logging +import platform from itertools import repeat from pathlib import Path from shutil import copyfile from typing import Optional -import platform from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import ( @@ -58,14 +58,18 @@ class EVS(Processing): self, ): if not self.cod_bin or not Path(self.cod_bin).exists(): - if platform.system() == "Windows" and (self.cod_bin and Path(self.cod_bin).with_suffix(".exe").exists()): + if platform.system() == "Windows" and ( + self.cod_bin and Path(self.cod_bin).with_suffix(".exe").exists() + ): self.cod_bin = Path(self.cod_bin).with_suffix(".exe") else: raise FileNotFoundError( "The EVS encoder binary was not found! Please check the configuration." ) if not self.dec_bin or not Path(self.dec_bin).exists(): - if platform.system() == "Windows" and (self.dec_bin and Path(self.dec_bin).with_suffix(".exe").exists()): + if platform.system() == "Windows" and ( + self.dec_bin and Path(self.dec_bin).with_suffix(".exe").exists() + ): self.dec_bin = Path(self.dec_bin).with_suffix(".exe") else: raise FileNotFoundError( diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 18199f9b..d7709360 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -32,10 +32,10 @@ import logging import os.path +import platform from copy import deepcopy from pathlib import Path from typing import Optional -import platform from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import parse_wave_header @@ -53,14 +53,18 @@ class IVAS(Processing): def _validate(self): if not self.cod_bin or not Path(self.cod_bin).exists(): - if platform.system() == "Windows" and (self.cod_bin and Path(self.cod_bin).with_suffix(".exe").exists()): + if platform.system() == "Windows" and ( + self.cod_bin and Path(self.cod_bin).with_suffix(".exe").exists() + ): self.cod_bin = Path(self.cod_bin).with_suffix(".exe") else: raise FileNotFoundError( "The IVAS encoder binary was not found! Please check the configuration." ) if not self.dec_bin or not Path(self.dec_bin).exists(): - if platform.system() == "Windows" and (self.dec_bin and Path(self.dec_bin).with_suffix(".exe").exists()): + if platform.system() == "Windows" and ( + self.dec_bin and Path(self.dec_bin).with_suffix(".exe").exists() + ): self.dec_bin = Path(self.dec_bin).with_suffix(".exe") else: raise FileNotFoundError( diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 1680cde0..847a8d11 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -140,9 +140,7 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): out_meta = [] if num_splits <= 1: - logger.info( - f"No splitting of output file necessary since only one signal used." - ) + logger.info("No splitting of output file necessary since only one signal used.") out_meta = None else: @@ -150,7 +148,9 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): for odir in cfg.out_dirs: path_input = odir / cfg.items_list[0].name - out_paths = split(path_input, odir, cfg.split_names, cfg.splits, preamble=cfg.preamble) + out_paths = split( + path_input, odir, cfg.split_names, cfg.splits, preamble=cfg.preamble + ) logger.debug( f"Resulting split files condition {odir.name}: {', '.join([str(op) for op in out_paths])}" @@ -162,7 +162,12 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): for odir in cfg.out_dirs: path_input = odir / cfg.items_list[0].name out_meta_paths = split_meta_in_file( - path_input, odir, cfg.split_names, cfg.splits, output_format, preamble=cfg.preamble + path_input, + odir, + cfg.split_names, + cfg.splits, + output_format, + preamble=cfg.preamble, ) out_meta.append(out_meta_paths) -- GitLab From 776ddd1e512bd0e16398a7c2665d2334ae07b03d Mon Sep 17 00:00:00 2001 From: Treffehn Date: Thu, 13 Apr 2023 15:59:58 +0200 Subject: [PATCH 04/31] clean up and documentation --- ivas_processing_scripts/audiotools/audio.py | 1 + .../binaural_datasets/HRIR_ORANGE53_FULL.mat | 4 +- .../audiotools/binaural_datasets/README.txt | 9 +- .../binaural_datasets/binaural_dataset.py | 13 +- .../audiotools/convert/__init__.py | 10 +- .../audiotools/convert/channelbased.py | 151 ++++++++++-------- .../audiotools/convert/masa.py | 45 +++++- .../audiotools/convert/objectbased.py | 56 ++++++- .../audiotools/convert/scenebased.py | 91 +++++++++-- .../audiotools/wrappers/bs1770.py | 10 +- .../audiotools/wrappers/esdru.py | 10 +- .../audiotools/wrappers/filter.py | 24 ++- .../audiotools/wrappers/masaRenderer.py | 3 +- .../audiotools/wrappers/p50fbmnru.py | 3 +- ivas_processing_scripts/bin/README.txt | 2 +- 15 files changed, 301 insertions(+), 131 deletions(-) diff --git a/ivas_processing_scripts/audiotools/audio.py b/ivas_processing_scripts/audiotools/audio.py index 1199889c..f6c45fca 100755 --- a/ivas_processing_scripts/audiotools/audio.py +++ b/ivas_processing_scripts/audiotools/audio.py @@ -214,6 +214,7 @@ class MetadataAssistedSpatialAudio(Audio): raise ValueError( f"Unsupported metadata assisted spatial audio format {name}" ) + self.metadata_files = [] @classmethod def _from_file( diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_FULL.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_FULL.mat index 7fd6e3d1..a2ab24e5 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_FULL.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_FULL.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a88a3463513647455bcc38bd7180860edfb97195602a8ff832a6be1421474f8 -size 14335861 +oid sha256:bf86a03f0b13932c5c138af22584f864b75c5733df1b01ac3fdf7750a1bdbe5f +size 14335913 diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/README.txt b/ivas_processing_scripts/audiotools/binaural_datasets/README.txt index e0836074..9fd37c96 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/README.txt +++ b/ivas_processing_scripts/audiotools/binaural_datasets/README.txt @@ -1,8 +1,9 @@ Files in this directory should contain impulse responses for use in rendering in Matlab .mat format +Samplingrate of 48kHz is assumed Files should adhere to the following naming scheme: -{HRIR|BRIR}_{DATASETNAME}_{FULL|LS|SBA3}.mat +{HRIR|BRIR}_{DATASETNAME}_{FULL|LS|SBA(1-3)}.mat - HRIR or BRIR specifies the type of impulse response which will be used @@ -15,7 +16,9 @@ Files should adhere to the following naming scheme: FULL: all available measurements on the sphere LS: superset of supported loudspeaker layouts (see audiotools.constants.CHANNEL_BASED_AUDIO_FORMATS["LS""]) - SBA3: impulse responses transformed to 3rd order ambisonics by external conversion + SBA(1-3): impulse responses transformed to ambisonics by external conversion + if available SBA1 is used for FOA, SBA2 for HOA2 and SBA3 for HOA3 + if not available SBA3 is used and truncated for all Ambisonic formats Each Matlab file should contain the following variables: - IR @@ -24,7 +27,7 @@ Each Matlab file should contain the following variables: array of {azimuth, elevation, radius} of dimensions [n_channels x 3] required for FULL, optional otherwise - latency_s - latency of the dataset in seconds + latency of the dataset in samples optional, will be estimated if not provided LICENSES: diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py index ab86d66a..ffab3c01 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py +++ b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py @@ -161,18 +161,17 @@ def load_ir( else: dataset_suffix = "SBA3" - IR, SourcePosition, latency_s = load_hrtf( - Path(__file__).parent.joinpath( - f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat" - ) + path_dataset = Path(__file__).parent.joinpath( + f"{dataset_prefix}_{dataset}_{dataset_suffix}.mat" ) + IR, SourcePosition, latency_s = load_hrtf(path_dataset) if latency_s is not None: - latency_smp = int(latency_s * 48000) + latency_smp = latency_s[0, 0] else: latency_smp = int(np.min(np.argmax(np.sum(np.abs(IR), axis=1), axis=0))) warnings.warn( - f"No latency of HRTF dataset specified in .mat file -> computed latency: {latency_smp}" + f"No latency of HRTF dataset specified in {path_dataset} file -> computed latency: {latency_smp} sample(s)" ) if in_fmt.startswith("STEREO"): @@ -182,7 +181,6 @@ def load_ir( and not in_fmt.startswith("CUSTOM_LS") and not in_fmt.startswith("MOZART") ): - # TODO update, use _get_audio_dict() instead of using fromtype object? # extract positions from the loudspeaker file in_fmt = fromtype(in_fmt) tmp_fmt = fromtype("LS") @@ -200,7 +198,6 @@ def load_ir( if j != in_fmt.lfe_index[0]: IR[:, :, ir_index] = IR_tmp[:, :, i] ir_index += 1 - # TODO: add custom ls support return IR, SourcePosition, latency_smp diff --git a/ivas_processing_scripts/audiotools/convert/__init__.py b/ivas_processing_scripts/audiotools/convert/__init__.py index d92c2b3a..024faa47 100755 --- a/ivas_processing_scripts/audiotools/convert/__init__.py +++ b/ivas_processing_scripts/audiotools/convert/__init__.py @@ -62,7 +62,9 @@ def convert_file( in_meta: Optional[list] = None, logger: Optional[logging.Logger] = None, **kwargs, -): +) -> None: + """Conversion function for one audio file""" + if not in_fmt: raise ValueError("Input audio format must be specified!") @@ -149,7 +151,9 @@ def convert( esdru_alpha: Optional[float] = None, logger: Optional[logging.Logger] = None, **kwargs, -): +) -> None: + """Perform pre-processing, conversion and post-processing""" + """pre-processing""" process_audio( x=input, @@ -203,6 +207,8 @@ def process_audio( esdru_alpha: Optional[float] = None, logger: Optional[logging.Logger] = None, ) -> None: + """Perform (pre-/pos-) processing of audio""" + if fs is None: fs = x.fs diff --git a/ivas_processing_scripts/audiotools/convert/channelbased.py b/ivas_processing_scripts/audiotools/convert/channelbased.py index b0fadf99..6bdd6b33 100755 --- a/ivas_processing_scripts/audiotools/convert/channelbased.py +++ b/ivas_processing_scripts/audiotools/convert/channelbased.py @@ -113,7 +113,6 @@ def render_cba_to_binaural( bin.audio = cba_stereo.audio return - # TODO this will change if we have resampled HRTFs cba.audio = resample_itu(cba, 48000) old_fs = cba.fs cba.fs = 48000 @@ -142,72 +141,81 @@ def render_cba_to_binaural( bin.audio = resample_itu(bin, old_fs) -# TODO rework impl. -# def render_custom_ls_binaural( -# custom_ls: audio.ChannelBasedAudio, -# output: audio.BinauralAudio, -# IR: np.ndarray, -# SourcePosition: np.ndarray, -# trajectory: str, -# ): - -# # logger.info(" Processing channels on custom LS layout") -# # azis = ", ".join([f"{a:7.2f}" for a in ls_azi_all]) -# # eles = ", ".join([f"{e:7.2f}" for e in ls_ele_all]) -# # logger.info(f" azi: {azis}") -# # logger.info(f" ele: {eles}") -# # logger.info(f" lfe_index: {lfe_index_all}") - -# if output.name == "BINAURAL_ROOM": -# tmp = get_audio_type("MOZART") -# convert_channel_based(custom_ls, tmp) -# logger.info(f" {custom_ls.name} -> {tmp.name} -> {output.name}") -# custom_ls.audio = tmp.audio -# else: -# tmp = custom_ls - -# ls_azi_all = tmp.ls_azi -# ls_ele_all = tmp.ls_ele -# lfe_index_all = tmp.lfe_index - -# frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000) -# sig_len = custom_ls.audio.shape[0] -# N_frames = int(sig_len / frame_len) - -# i_ls = 0 -# y = np.zeros([sig_len, 2]) -# for i_chan in range(custom_ls.audio.shape[1]): - -# # skip LFE -# if i_chan in lfe_index_all: -# continue - -# # skip silent (or very low volume) channels -# if np.allclose(custom_ls.audio[:, i_chan], 0.0, atol=32.0): -# continue - -# ls_azi = np.repeat(ls_azi_all[i_ls], N_frames) -# ls_ele = np.repeat(ls_ele_all[i_ls], N_frames) - -# azi, ele = rotateISM(ls_azi, ls_ele, trajectory=trajectory) - -# # TODO: use EFAP here -# y += binaural_fftconv_framewise( -# custom_ls.audio[:, i_chan], -# IR, -# SourcePosition, -# frame_len=frame_len, -# azi=azi, -# ele=ele, -# ) -# i_ls += 1 - -# return y +def render_custom_ls_binaural( + custom_ls: audio.ChannelBasedAudio, + output: audio.BinauralAudio, + IR: np.ndarray, + SourcePosition: np.ndarray, + trajectory: str, +): + # TODO rework impl. (with EFAP) + # logger.info(" Processing channels on custom LS layout") + # azis = ", ".join([f"{a:7.2f}" for a in ls_azi_all]) + # eles = ", ".join([f"{e:7.2f}" for e in ls_ele_all]) + # logger.info(f" azi: {azis}") + # logger.info(f" ele: {eles}") + # logger.info(f" lfe_index: {lfe_index_all}") + + # if output.name == "BINAURAL_ROOM": + # tmp = get_audio_type("MOZART") + # convert_channel_based(custom_ls, tmp) + # logger.info(f" {custom_ls.name} -> {tmp.name} -> {output.name}") + # custom_ls.audio = tmp.audio + # else: + # tmp = custom_ls + # + # ls_azi_all = tmp.ls_azi + # ls_ele_all = tmp.ls_ele + # lfe_index_all = tmp.lfe_index + # + # frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000) + # sig_len = custom_ls.audio.shape[0] + # N_frames = int(sig_len / frame_len) + # + # i_ls = 0 + # y = np.zeros([sig_len, 2]) + # for i_chan in range(custom_ls.audio.shape[1]): + # + # # skip LFE + # if i_chan in lfe_index_all: + # continue + # + # # skip silent (or very low volume) channels + # if np.allclose(custom_ls.audio[:, i_chan], 0.0, atol=32.0): + # continue + # + # ls_azi = np.repeat(ls_azi_all[i_ls], N_frames) + # ls_ele = np.repeat(ls_ele_all[i_ls], N_frames) + # + # azi, ele = rotateISM(ls_azi, ls_ele, trajectory=trajectory) + # + # y += binaural_fftconv_framewise( + # custom_ls.audio[:, i_chan], + # IR, + # SourcePosition, + # frame_len=frame_len, + # azi=azi, + # ele=ele, + # ) + # i_ls += 1 + # + # return y + return def render_cba_to_cba( cba_in: audio.ChannelBasedAudio, cba_out: audio.ChannelBasedAudio ) -> None: + """ + Rendering of channel-based input signal to channel-based output + + Parameters + ---------- + cba_in: audio.ObjectBasedAudio + Channel-based input audio + cba_out: audio.ChannelBasedAudio + Channel-based output audio + """ # Stereo to Mono if cba_in.name == "STEREO" and cba_out.name == "MONO": @@ -229,14 +237,13 @@ def render_cba_to_cba( if i not in cba_in.lfe_index ] ) - # TODO tmu : implement configurable LFE handling + # pass-through for LFE for index in np.sort(cba_in.lfe_index): render_mtx = np.insert(render_mtx, index, 0, axis=0) render_mtx = np.insert(render_mtx, cba_out.lfe_index, 0, axis=1) render_mtx[cba_in.lfe_index, cba_out.lfe_index] = 1 - # TODO tmu temporarily disable LFE rendering to MONO/STEREO if cba_out.num_channels <= 2: render_mtx[cba_in.lfe_index, :] = 0 @@ -244,6 +251,16 @@ def render_cba_to_cba( def render_cba_to_sba(cba: audio.ChannelBasedAudio, sba: audio.SceneBasedAudio) -> None: + """ + Rendering of channel-based input signal to SBA output + + Parameters + ---------- + cba: audio.ObjectBasedAudio + Channel-based input audio + sba: audio.ChannelBasedAudio + SBA output audio + """ if cba.name == "MONO": raise ValueError(f"Rendering from MONO to {sba.name} is not supported.") @@ -284,7 +301,6 @@ def rotate_cba( Rotated multichannel signal """ - # TODO needs optimization, currently slow trj_data = np.genfromtxt(trajectory, delimiter=",") trj_frames = trj_data.shape[0] @@ -346,8 +362,6 @@ def render_lfe_to_binaural( if lfe.shape[1] > 1: lfe = np.sum(lfe, axis=1) - # TODO tmu - disabled temporarily here, disabled in C - # TODO: add delay compensation """ # 120 Hz low-pass filtering for LFE using IVAS filter coefficients if fs == 48000: @@ -363,6 +377,7 @@ def render_lfe_to_binaural( lfe = np.roll(lfe, round(latency_smp), axis=0) lfe[0 : round(latency_smp), :] = 0 """ + lfe_delay_ns = 0 # apply gain lfe *= LFE_gain @@ -372,4 +387,4 @@ def render_lfe_to_binaural( lfe = lfe[:, np.newaxis] lfe = np.hstack([lfe, lfe]) - return lfe + return lfe, lfe_delay_ns diff --git a/ivas_processing_scripts/audiotools/convert/masa.py b/ivas_processing_scripts/audiotools/convert/masa.py index c03977ab..c802a78b 100755 --- a/ivas_processing_scripts/audiotools/convert/masa.py +++ b/ivas_processing_scripts/audiotools/convert/masa.py @@ -30,7 +30,6 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # -import logging from pathlib import Path from typing import Optional, Union from warnings import warn @@ -45,7 +44,6 @@ from ivas_processing_scripts.audiotools.wrappers.masaRenderer import masaRendere def convert_masa( masa: audio.MetadataAssistedSpatialAudio, out: audio.Audio, - logger: Optional[logging.Logger] = None, **kwargs, ) -> audio.Audio: """Convert Metadata Assisted Spatial audio to the requested output format""" @@ -76,7 +74,22 @@ def render_masa_to_binaural( trajectory: Optional[Union[str, Path]] = None, bin_dataset: Optional[str] = None, **kwargs, -): +) -> None: + """ + Binauralization of MASA audio + + Parameters + ---------- + masa: audio.MetadataAssistedSpatialAudio + MASA input audio + bin: audio.BinauralAudio + Output binaural audio + trajectory: Optional[Union[str, Path]] + Head rotation trajectory path + bin_dataset: Optional[str] + Name of binaural dataset without prefix or suffix + """ + if "ROOM" in bin.name: cba_tmp = audio.fromtype("7_1_4") cba_tmp.fs = masa.fs @@ -100,7 +113,18 @@ def render_masa_to_binaural( def render_masa_to_cba( masa: audio.MetadataAssistedSpatialAudio, cba: audio.ChannelBasedAudio, -): +) -> None: + """ + Rendering of MASA input signal to Channel-based format + + Parameters + ---------- + masa: audio.MetadataAssistedSpatialAudio + MASA input audio + cba: audio.ChannelBasedAudio + Channel-based output audio + """ + if cba.name not in ["5_1", "7_1_4"]: warn( f"MasaRenderer does not support {cba.name} natively. Using 7_1_4 as an intermediate format." @@ -118,7 +142,18 @@ def render_masa_to_cba( def render_masa_to_sba( masa: audio.MetadataAssistedSpatialAudio, sba: audio.SceneBasedAudio, -): +) -> None: + """ + Rendering of MASA input signal to SBA format + + Parameters + ---------- + masa: audio.MetadataAssistedSpatialAudio + MASA input audio + sba: audio.SceneBasedAudio + SBA output audio + """ + warn( f"MasaRenderer does not support {sba.name} natively. Using 7_1_4 as an intermediate format." ) diff --git a/ivas_processing_scripts/audiotools/convert/objectbased.py b/ivas_processing_scripts/audiotools/convert/objectbased.py index 4111face..c6d0f114 100755 --- a/ivas_processing_scripts/audiotools/convert/objectbased.py +++ b/ivas_processing_scripts/audiotools/convert/objectbased.py @@ -261,7 +261,25 @@ def rotate_oba( ele: np.ndarray, trajectory: Optional[str] = None, ) -> Tuple[np.ndarray, np.ndarray]: - """Application of head tracking trajectory""" + """ + Application of head tracking trajectory + + Parameters: + ---------- + azi: np.ndarray + Azimuth coordinates of objects + ele: np.ndarray + Elevation coordinates of objects + trajectory: str + Head-tracking trajectory path + + Returns: + ---------- + azi_rot: np.ndarray + Azimuth coordinates after application of trajectory + ele_rot: np.ndarray + Elevation coordinates after application of trajectory + """ if trajectory is None: return azi, ele @@ -286,14 +304,36 @@ def rotate_oba( def render_object( - obj_idx, - obj_pos, - oba, - trajectory, - IR, - SourcePosition, + obj_idx: int, + obj_pos: np.ndarray, + oba: audio.ObjectBasedAudio, + trajectory: str, + IR: np.ndarray, + SourcePosition: np.ndarray, ) -> np.ndarray: - """Binaural rendering for one ISM object""" + """ + Binaural rendering for one ISM object + + Parameters: + ---------- + obj_idx: int + Index of object in list of all objects + obj_pos: np.ndarray + Position of object + oba: audio.ObjectBasedAudio + Input ISM audio object + trajectory: str + Head-tracking trajectory path + IR: np.ndarray + HRIRs for binauralization + SourcePosition: np.ndarray + Positions of HRIR measurements + + Returns: + ---------- + result_audio: np.ndarray + Binaurally rendered object + """ # repeat each value four times since head rotation data is on sub-frame basis azi = np.repeat(obj_pos[:, 0], 4) diff --git a/ivas_processing_scripts/audiotools/convert/scenebased.py b/ivas_processing_scripts/audiotools/convert/scenebased.py index 10a91522..b8295808 100755 --- a/ivas_processing_scripts/audiotools/convert/scenebased.py +++ b/ivas_processing_scripts/audiotools/convert/scenebased.py @@ -83,10 +83,6 @@ def convert_scenebased( return out -def zero_vert_channels(sba: audio.SceneBasedAudio): - sba.audio[:, VERT_HOA_CHANNELS_ACN[VERT_HOA_CHANNELS_ACN < sba.num_channels]] = 0 - - def render_sba_to_binaural( sba: audio.SceneBasedAudio, bin: audio.BinauralAudio, @@ -106,7 +102,7 @@ def render_sba_to_binaural( trajectory: Optional[Union[str, Path]] Head rotation trajectory path bin_dataset: Optional[str] - Name of binaural dataset wihtout prefix or suffix + Name of binaural dataset without prefix or suffix """ if trajectory is not None: @@ -138,7 +134,18 @@ def render_sba_to_binaural( def render_sba_to_cba( sba: audio.SceneBasedAudio, cba: audio.ChannelBasedAudio, -): +) -> None: + """ + Rendering of SBA input signal to channel-based format + + Parameters + ---------- + sba: audio.SceneBasedAudio + Scene-based input audio + cba: audio.ChannelBasedAudio + Channel-based output audio + """ + render_mtx = get_allrad_mtx(sba.ambi_order, cba) cba.audio = sba.audio @ render_mtx.T @@ -147,6 +154,17 @@ def render_sba_to_sba( sba_in: audio.SceneBasedAudio, sba_out: audio.SceneBasedAudio, ) -> None: + """ + Rendering of SBA input signal to SBA output format + + Parameters + ---------- + sba_in: audio.SceneBasedAudio + Scene-based input audio + sba_out: audio.SceneBasedAudio + Scene-based output audio + """ + if sba_out.ambi_order > sba_in.ambi_order: sba_out.audio = np.pad( sba_in.audio, [[0, 0], [0, sba_out.num_channels - sba_in.num_channels]] @@ -218,15 +236,23 @@ def rotate_sba( """ Helper functions """ -def nchan_from_ambi_order(ambi_order: int): +def zero_vert_channels(sba: audio.SceneBasedAudio) -> None: + """Remove all ambisonics parts with vertical components""" + sba.audio[:, VERT_HOA_CHANNELS_ACN[VERT_HOA_CHANNELS_ACN < sba.num_channels]] = 0 + + +def nchan_from_ambi_order(ambi_order: int) -> int: + """Compute number of channels based on ambisonics order""" return (ambi_order + 1) ** 2 -def ambi_order_from_nchan(nchan: int): +def ambi_order_from_nchan(nchan: int) -> int: + """Compute ambisonics order based on number of channels""" return int(np.sqrt(nchan) - 1) def rE_weight(order: int) -> np.ndarray: + """Compute max-rE weighting matrix""" return np.array( [ lpmv(0, l, np.cos(np.deg2rad(137.9) / (order + 1.51))) @@ -237,12 +263,14 @@ def rE_weight(order: int) -> np.ndarray: def n2sn(order: int) -> np.ndarray: + """Compute conversion matrix for N3D to SN3D normalization""" return np.array( [1.0 / np.sqrt(2 * l + 1) for l in range(order + 1) for _ in range(-l, l + 1)] ) def sn2n(order: int) -> np.ndarray: + """Compute conversion matrix for SN3D to N3D normalization""" return np.array( [np.sqrt(2 * l + 1) for l in range(order + 1) for _ in range(-l, l + 1)] ) @@ -257,7 +285,27 @@ def getRSH( ) -> np.ndarray: """ Returns real spherical harmonic response for the given position(s) + + Parameters: + ---------- + azi: np.ndarray + Azimuth angles + ele: np.ndarray + Elevation angles + ambi_order: int + Ambisonics order + norm: Optional[str] + Normalization of ambisonic bases. + Possible values: "sn3d", "n3d", everything else is interpreted as orthogonal + degrees: Optional[bool] + If true azi and ele are interpreted as angles in degrees, otherwise as radians + + Returns: + ---------- + response: np.ndarray + Real spherical harmonic response """ + if degrees: azi = np.deg2rad(azi) ele = np.deg2rad(ele) @@ -310,9 +358,32 @@ def get_allrad_mtx( ambi_order: int, cba: audio.ChannelBasedAudio, norm: Optional[str] = "sn3d", - rE_weight: Optional[bool] = False, + rE_weight_bool: Optional[bool] = False, intensity_panning: Optional[bool] = True, ) -> np.ndarray: + """ + Returns ALLRAD matrix + + Parameters: + ---------- + ambi_order: int + Ambisonics order + cba: audio.ChannelBasedAudio + Channel-based audio object + norm: Optional[str] + Normalization of ambisonic bases. + Possible values: "sn3d", "ortho", everything else is interpreted as n3d + re_weight_bool: Optional[bool] + Flag for max-rE weighting + intensity_panning: Optional[bool] + Flag for intensity panning + + Returns: + ---------- + hoa_dec: np.ndarray + ALLRAD matrix + """ + n_harm = nchan_from_ambi_order(ambi_order) if cba.name == "MONO": @@ -348,7 +419,7 @@ def get_allrad_mtx( elif norm == "ortho": hoa_dec *= np.sqrt(4 * np.pi) - if rE_weight: + if rE_weight_bool: a_n = rE_weight(ambi_order) nrg_pre = np.sqrt(len(n_ls_woLFE) / np.sum(a_n**2)) hoa_dec = hoa_dec @ np.diag(a_n) * nrg_pre diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index d7c8739a..3a42658b 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -108,7 +108,7 @@ def bs1770demo( ] if isinstance(input, audio.BinauralAudio): - cmd[6] = "00" # "11" # -conf + cmd[6] = "00" # -conf elif isinstance(input, audio.ChannelBasedAudio): # if loudspeaker position fulfills the criteria, set the config string to 1 for that index conf_str = [ @@ -148,7 +148,7 @@ def get_loudness( target_loudness: float Desired loudness in LKFS loudness_format: str - Loudness format to render to for loudness computation (default input format) + Loudness format to render to for loudness computation (default input format if possible) Returns ------- @@ -162,6 +162,7 @@ def get_loudness( raise ValueError("Desired loudness is too high!") if loudness_format is None: + # for some formats rendering is necessary prior to loudness measurement if isinstance(input, audio.SceneBasedAudio) or isinstance( input, audio.MetadataAssistedSpatialAudio ): @@ -171,6 +172,7 @@ def get_loudness( elif hasattr(input, "layout_file"): loudness_format = input.layout_file else: + # default use input format loudness_format = input.name # configure intermediate format @@ -200,7 +202,7 @@ def loudness_norm( input : Audio Input audio target_loudness: Optional[float] - Desired loudness level in LKFS/dBov + Desired loudness level in LKFS loudness_format: Optional[str] Loudness format to render to for loudness computation (default input format) @@ -228,7 +230,7 @@ def loudness_norm( num_iter += 1 - if num_iter > 10: + if num_iter >= 10: warn( f"Loudness did not converge to desired value, stopping at: {measured_loudness:.2f}" ) diff --git a/ivas_processing_scripts/audiotools/wrappers/esdru.py b/ivas_processing_scripts/audiotools/wrappers/esdru.py index a26ff511..4e0dfbea 100755 --- a/ivas_processing_scripts/audiotools/wrappers/esdru.py +++ b/ivas_processing_scripts/audiotools/wrappers/esdru.py @@ -32,7 +32,7 @@ from pathlib import Path from tempfile import TemporaryDirectory -from typing import Optional, Tuple +from typing import Optional import numpy as np @@ -47,7 +47,7 @@ def esdru( sf: Optional[int] = 48000, e_step: Optional[float] = 0.5, seed: Optional[int] = 1, -) -> Tuple[np.ndarray, int]: +) -> np.ndarray: """ Wrapper for ESDRU (Ericsson spatial distortion reference unit) Recommendation ITU-T P.811, requires esdru binary @@ -72,8 +72,10 @@ def esdru( binary = find_binary("esdru") - if input.num_channels != 2: - raise Exception("Input audio is not stereo.") + if not isinstance(input, audio.BinauralAudio) and not input.name == "STEREO": + raise Exception( + "ESDRU condition only available for STEREO or BINAURAL output format" + ) if alpha <= 0.0 or alpha >= 1.0: raise Exception( diff --git a/ivas_processing_scripts/audiotools/wrappers/filter.py b/ivas_processing_scripts/audiotools/wrappers/filter.py index fa58200b..1efbf9be 100755 --- a/ivas_processing_scripts/audiotools/wrappers/filter.py +++ b/ivas_processing_scripts/audiotools/wrappers/filter.py @@ -197,8 +197,6 @@ def lpfilter_itu( Output low-pass filtered array """ - # TODO: change filter functions such that audio is modified -> no return value - # find right filter type for cut-off frequency flt_types = ["LP1p5", "LP35", "LP7", "LP10", "LP12", "LP14", "LP20"] flt_vals = [1500, 3500, 7000, 10000, 12000, 14000, 20000] @@ -291,7 +289,7 @@ def resample_itu( fs_new: int, ) -> np.ndarray: """ - Resampling of multichannel audio signal + Resampling of multi-channel audio array Parameters ---------- @@ -347,15 +345,15 @@ def resample_itu( y.audio = delay_compensation( y.audio, flt_type=flt, fs=y.fs, up=up[i], down=down[i] ) - if up[i]: - if flt == "SHQ2": - y.fs = y.fs * 2 - elif flt == "SHQ3": - y.fs = y.fs * 3 - elif down[i]: - if flt == "SHQ2": - y.fs = int(y.fs / 2) - elif flt == "SHQ3": - y.fs = int(y.fs / 3) + # if up[i]: + # if flt == "SHQ2": + # y.fs = y.fs * 2 + # elif flt == "SHQ3": + # y.fs = y.fs * 3 + # elif down[i]: + # if flt == "SHQ2": + # y.fs = int(y.fs / 2) + # elif flt == "SHQ3": + # y.fs = int(y.fs / 3) return y.audio diff --git a/ivas_processing_scripts/audiotools/wrappers/masaRenderer.py b/ivas_processing_scripts/audiotools/wrappers/masaRenderer.py index 682fc91a..7b5eafda 100755 --- a/ivas_processing_scripts/audiotools/wrappers/masaRenderer.py +++ b/ivas_processing_scripts/audiotools/wrappers/masaRenderer.py @@ -60,6 +60,7 @@ def masaRenderer( output : np.ndarray MASA rendered to out_fmt """ + binary = find_binary("masaRenderer") if out_fmt not in ["5_1", "7_1_4", "BINAURAL"]: @@ -79,7 +80,7 @@ def masaRenderer( str(binary), output_mode, "", # 2 -> inputPcm - str(masa.metadata_file.resolve()), + str(masa.metadata_files.resolve()), "", # 4 -> outputPcm ] diff --git a/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py b/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py index 0c67b679..76601048 100755 --- a/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py +++ b/ivas_processing_scripts/audiotools/wrappers/p50fbmnru.py @@ -32,7 +32,6 @@ from pathlib import Path from tempfile import TemporaryDirectory -from typing import Tuple from warnings import warn import numpy as np @@ -46,7 +45,7 @@ from ivas_processing_scripts.utils import find_binary, run def p50fbmnru( input: audio.Audio, q_db: float, -) -> Tuple[np.ndarray, int]: +) -> np.ndarray: """ Wrapper for P.50 Fullband MNRU (Modulated Noise Reference Unit), requires p50fbmnru binary The mode is M (Modulated Noise) as specified in section 5.2.1 of S4-141392 - EVS-7c Processing functions for characterization phase v110.doc diff --git a/ivas_processing_scripts/bin/README.txt b/ivas_processing_scripts/bin/README.txt index 00bdbec2..0aa3037d 100755 --- a/ivas_processing_scripts/bin/README.txt +++ b/ivas_processing_scripts/bin/README.txt @@ -1,6 +1,6 @@ Necessary binaries/executables: -- ITU tools: https://github.com/openitu/STL +- ITU tools: https://github.com/openitu/STL (deprecated filter function) or https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip - bs1770demo used for loudness measurement and adjustment - filter used for 50Hz high-pass filtering, 3.5kHz and 7kHz low-pass filtering and resampling - esdru used for ESDRU condition -- GitLab From 4eb9496d2880a6cd320c6856edc9624a8c23026c Mon Sep 17 00:00:00 2001 From: Treffehn Date: Thu, 13 Apr 2023 18:14:11 +0200 Subject: [PATCH 05/31] small changes --- .../audiotools/audioarray.py | 7 +++ .../binaural_datasets/HRIR_ORANGE53_LS.mat | 4 +- .../audiotools/metadata.py | 49 +++++++++++++++++-- .../audiotools/rotation.py | 8 +-- ivas_processing_scripts/constants.py | 2 +- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/ivas_processing_scripts/audiotools/audioarray.py b/ivas_processing_scripts/audiotools/audioarray.py index 0cc88f77..76c7d81d 100755 --- a/ivas_processing_scripts/audiotools/audioarray.py +++ b/ivas_processing_scripts/audiotools/audioarray.py @@ -31,6 +31,7 @@ # import logging +import warnings from typing import Iterator, Optional, Tuple, Union import numpy as np @@ -268,6 +269,7 @@ def limiter( release_heuristics_mem = 0.0 gain = 1.0 strong_saturation_cnt = 0 + limited = False if x.ndim == 1: n_samples_x = x.shape @@ -326,16 +328,21 @@ def limiter( fr_gain = np.tile(gain * fac + frame_gain * (1.0 - fac), (n_chan_x, 1)).T fr_sig *= fr_gain gain = fr_gain[-1, 0] + limited = True else: gain = 1.0 release_heuristics_mem = release_heuristic # hard limiting for everything that still sticks out + if (fr_sig > 32767).any() or (fr_sig < -32768).any(): + limited = True idx_max = np.where(fr_sig > 32767) fr_sig[idx_max] = 32767 idx_min = np.where(fr_sig < -32768) fr_sig[idx_min] = -32768 + if limited: + warnings.warn("Limiting had to be applied") return x diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_LS.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_LS.mat index e52e031e..65c2684c 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_LS.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_LS.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9660be83192f7babb4f67e19653a94bc02cee7b3071065880cf618547c19d842 -size 20138 +oid sha256:2e25ef101e9e72c5d70a55bc1451a07d041d29f96a803d7d3f968f20fe403316 +size 20190 diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index 00cf8601..db585ac0 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -146,7 +146,6 @@ class Metadata: self.audio.append(sba) def parse_optional_values(self, f: TextIO): - # TODO implementation raise NotImplementedError( "Additional configuration keys in metadata currently unsupported!" ) @@ -223,6 +222,22 @@ def trim_meta( pad_noise: Optional[bool] = False, samples: Optional[bool] = False, ) -> None: + """ + Trim or pad ISM including metadata + positive limits trim negative limits pad + + Parameters + ---------- + x: audio.ObjectBasedAudio + ISM audio object + limits: Optional[Tuple[int, int]] + Number of samples to trim or pad at beginning and end + pad_noise: Optional[bool] + Flag for padding noise instead of silence + samples: Optional[bool] + Flag for interpreting limits as samples, otherwise milliseconds + """ + if not limits: return @@ -275,7 +290,7 @@ def trim_meta( if trim_frames_pre < 0: x.object_pos[i][: abs(trim_frames_pre), 2] = 1 if trim_frames_post < 0: - x.object_pos[i][abs(trim_frames_post) :, 2] = 1 + x.object_pos[i][abs(trim_frames_post):, 2] = 1 return @@ -289,12 +304,32 @@ def concat_meta_from_file( input_fmt: str, preamble: Optional[int] = None, ) -> None: + """ + Concatenate ISM metadata from files + + Parameters + ---------- + audio_files: list[str] + List of audio file names + meta_files: list[list[str]] + List of corresponding metadata file names + out_file: list[str] + Name of concatenated output file + silence_pre: int + Silence inserted before each item + silence_post: int + Silence inserted after each item + input_fmt: str + Input audio format + preamble: Optional[int] + Length of preamble in milliseconds + """ + # create audio objects audio_objects = [] fs = None for i, audio_file in enumerate(audio_files): # metadata is cut/looped to signal length in init of audio object - # TODO check fs for audio object when pcm audio_object = audio.fromfile(input_fmt, audio_file, in_meta=meta_files[i]) audio_objects.append(audio_object) if fs: @@ -438,9 +473,13 @@ def split_meta_in_file( def check_ISM_metadata( - in_meta: dict, num_objects: int, num_items: int, item_names: Optional[list] = None + in_meta: dict, + num_objects: int, + num_items: int, + item_names: Optional[list] = None, ) -> list: """Find ISM metadata""" + list_meta = [] if in_meta is None: for item in item_names: @@ -493,7 +532,7 @@ def metadata_search( in_meta: Union[str, Path], item_names: list[Union[str, Path]], num_objects: int, -) -> list: +) -> list[list[Union[Path, str]]]: """Search for ISM metadata with structure item_name.{0-3}.csv in in_meta folder""" if not item_names: diff --git a/ivas_processing_scripts/audiotools/rotation.py b/ivas_processing_scripts/audiotools/rotation.py index 85d8bf53..742548a8 100755 --- a/ivas_processing_scripts/audiotools/rotation.py +++ b/ivas_processing_scripts/audiotools/rotation.py @@ -136,14 +136,14 @@ def SHrotmatgen( R: np.ndarray, order: Optional[int] = 3, ) -> np.ndarray: - """Calculate SHD rotation matrix from that in real space + """ + Calculate SHD rotation matrix from that in real space translated from ivas_rotation.c Parameters: ---------- R: np.ndarray real-space rotation matrix - order: Optional[int] Ambisonics order, default = 3 @@ -151,8 +151,8 @@ def SHrotmatgen( ---------- SHrotmat: np.ndarray SHD rotation matrix - """ + dim = (order + 1) * (order + 1) SHrotmat = np.zeros([dim, dim]) @@ -357,6 +357,8 @@ def rotateAziEle( R: np.ndarray, is_planar: bool = False, ) -> Tuple[float, float]: + """Rotate azimuth and elevation angles with rotation matrix""" + w = np.cos(np.deg2rad(ele)) dv = np.array( [ diff --git a/ivas_processing_scripts/constants.py b/ivas_processing_scripts/constants.py index ff56b9bf..16e7d264 100755 --- a/ivas_processing_scripts/constants.py +++ b/ivas_processing_scripts/constants.py @@ -69,7 +69,7 @@ DEFAULT_CONFIG = { # postprocessing "postprocessing": { "hp50": False, - "limit": True, + "limit": False, }, } DEFAULT_CONFIG_EVS = { -- GitLab From 8c0bfa267729089adc39ed753ef81d3b8ec30825 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Fri, 14 Apr 2023 12:12:24 +0200 Subject: [PATCH 06/31] added latency to new datasets --- .../audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA1.mat | 4 ++-- .../audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA2.mat | 4 ++-- .../audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA3.mat | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA1.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA1.mat index d30f7571..4f59a8a9 100644 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA1.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA1.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49fea9097dd01b0529cdbff150542f4612750ea03cdd75913e2d5bffcf284753 -size 4578 +oid sha256:3a9ad5d8d874ac2fb851f5d2b0b303494f1d115612e9f6cab40e5eb33591b05c +size 4630 diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA2.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA2.mat index 81450496..1ad2162a 100644 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA2.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA2.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:746b5c4b12010d77bd96cb05534eb2eba6b41381fe50949a2ca3df2c75a940ba -size 10271 +oid sha256:6fc2a15579b80493597a8096bd815e8b847fe1880bdba760d4405122878b0b0a +size 10323 diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA3.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA3.mat index ffa700ca..0e7c3ef4 100644 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA3.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_Dolby_SBA3.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7704ee5a72a3c051eef04f68f346811b550bde62144a0b71f2aa8fa35a931660 -size 18177 +oid sha256:83822cfa090c345a6ece14d1ec1a92023626f467e2f8d982cf099c071dfc1080 +size 18229 -- GitLab From 69b14afff350574de33307a5acf181de3f292c7f Mon Sep 17 00:00:00 2001 From: Treffehn Date: Fri, 14 Apr 2023 13:24:25 +0200 Subject: [PATCH 07/31] changed datatype of latency --- .../audiotools/binaural_datasets/binaural_dataset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py index ffab3c01..88c067a9 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py +++ b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py @@ -80,7 +80,7 @@ def load_hrtf( raise KeyError(f"Key 'IR' not found in .mat file: {filename} !") SourcePosition = mat_contents.get("SourcePosition") - latency_s = mat_contents.get("latency_s") + latency_s = mat_contents.get("latency_s").astype(np.int32)[0, 0] return IR, SourcePosition, latency_s @@ -167,7 +167,7 @@ def load_ir( IR, SourcePosition, latency_s = load_hrtf(path_dataset) if latency_s is not None: - latency_smp = latency_s[0, 0] + latency_smp = latency_s else: latency_smp = int(np.min(np.argmax(np.sum(np.abs(IR), axis=1), axis=0))) warnings.warn( -- GitLab From 5b73dc2ff28a9d66c7360587163afd2a46f8e6c9 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Fri, 14 Apr 2023 14:32:28 +0200 Subject: [PATCH 08/31] small change for latency --- .../audiotools/binaural_datasets/binaural_dataset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py index 88c067a9..e5d5ac95 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py +++ b/ivas_processing_scripts/audiotools/binaural_datasets/binaural_dataset.py @@ -80,7 +80,9 @@ def load_hrtf( raise KeyError(f"Key 'IR' not found in .mat file: {filename} !") SourcePosition = mat_contents.get("SourcePosition") - latency_s = mat_contents.get("latency_s").astype(np.int32)[0, 0] + latency_s = mat_contents.get("latency_s") + if latency_s is not None: + latency_s = latency_s.astype(np.int32)[0, 0] return IR, SourcePosition, latency_s -- GitLab From 33dfec0f2ac82645971abcc8eb2bf784fc2cd956 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Fri, 14 Apr 2023 15:30:16 +0200 Subject: [PATCH 09/31] added latency to BRIRs and removed old dataset --- .../binaural_datasets/BRIR_IISofficialMPEG222UC_FULL.mat | 4 ++-- .../binaural_datasets/BRIR_IISofficialMPEG222UC_LS.mat | 4 ++-- .../audiotools/binaural_datasets/HRIR_ORANGE53_SBA3.mat | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) delete mode 100755 ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_SBA3.mat diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_FULL.mat b/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_FULL.mat index c6549ece..42e702db 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_FULL.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_FULL.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ebbe3d45cd35e1fd8fb17896a7917b2de6dae03fc34c3a7350288b9a53c2e9d -size 12623137 +oid sha256:a3ddecef64dfcf8887904b5cc370c0d9723bd8fd1637e32232205cdcd739b80d +size 12623190 diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_LS.mat b/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_LS.mat index 61ba9466..1d590edb 100755 --- a/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_LS.mat +++ b/ivas_processing_scripts/audiotools/binaural_datasets/BRIR_IISofficialMPEG222UC_LS.mat @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:081a9053c8b04831d97e6f18d641d4737b2c23b076778a9b41c7b3a41d954c32 -size 6348446 +oid sha256:e2c964b96d802532c0ecf1076092c7d246a54293a3a0c4c72995953c66bfec71 +size 6348499 diff --git a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_SBA3.mat b/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_SBA3.mat deleted file mode 100755 index 0d113a34..00000000 --- a/ivas_processing_scripts/audiotools/binaural_datasets/HRIR_ORANGE53_SBA3.mat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:02c8a25178b36399054c1802f00bb5a8739f3ac950c21b0c760c046b1dba530d -size 36201 -- GitLab From b3c889a2642b9d1a3186ed3ed499c3799926cd2e Mon Sep 17 00:00:00 2001 From: Treffehn Date: Fri, 14 Apr 2023 17:22:56 +0200 Subject: [PATCH 10/31] added files for bitstream processing --- .../audiotools/metadata.py | 2 +- .../audiotools/wrappers/bs1770.py | 27 ++++---- .../audiotools/wrappers/ei-xor.py | 56 +++++++++++++++++ .../audiotools/wrappers/gen-patt.py | 62 +++++++++++++++++++ ivas_processing_scripts/error_patterns/sta_03 | 9 +++ ivas_processing_scripts/error_patterns/sta_05 | 9 +++ ivas_processing_scripts/error_patterns/sta_06 | 9 +++ ivas_processing_scripts/error_patterns/sta_10 | 9 +++ 8 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 ivas_processing_scripts/audiotools/wrappers/ei-xor.py create mode 100644 ivas_processing_scripts/audiotools/wrappers/gen-patt.py create mode 100644 ivas_processing_scripts/error_patterns/sta_03 create mode 100644 ivas_processing_scripts/error_patterns/sta_05 create mode 100644 ivas_processing_scripts/error_patterns/sta_06 create mode 100644 ivas_processing_scripts/error_patterns/sta_10 diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index db585ac0..d7fd167d 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -290,7 +290,7 @@ def trim_meta( if trim_frames_pre < 0: x.object_pos[i][: abs(trim_frames_pre), 2] = 1 if trim_frames_post < 0: - x.object_pos[i][abs(trim_frames_post):, 2] = 1 + x.object_pos[i][abs(trim_frames_post) :, 2] = 1 return diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index 3a42658b..4011d3eb 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -1,17 +1,4 @@ #!/usr/bin/env python3 -import copy -import logging -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import Optional, Tuple, Union -from warnings import warn - -import numpy as np - -from ivas_processing_scripts.audiotools import audio, convert -from ivas_processing_scripts.audiotools.audiofile import write -from ivas_processing_scripts.audiotools.wrappers.filter import resample_itu -from ivas_processing_scripts.utils import find_binary, get_devnull, run # # (C) 2022-2023 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, @@ -43,6 +30,19 @@ from ivas_processing_scripts.utils import find_binary, get_devnull, run # the United Nations Convention on Contracts on the International Sales of Goods. # +import copy +import logging +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Optional, Tuple, Union +from warnings import warn + +import numpy as np + +from ivas_processing_scripts.audiotools import audio, convert +from ivas_processing_scripts.audiotools.audiofile import write +from ivas_processing_scripts.audiotools.wrappers.filter import resample_itu +from ivas_processing_scripts.utils import find_binary, get_devnull, run logger = logging.getLogger("__main__") logger.setLevel(logging.DEBUG) @@ -271,7 +271,6 @@ def scale_files( for folder, meta_folder in zip(file_list, in_meta): for file, meta in zip(folder, meta_folder): - # create audio object if meta_bool: audio_obj = audio.fromfile(fmt, file, fs, meta) diff --git a/ivas_processing_scripts/audiotools/wrappers/ei-xor.py b/ivas_processing_scripts/audiotools/wrappers/ei-xor.py new file mode 100644 index 00000000..c6627332 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/ei-xor.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 +from typing import Union + +from ivas_processing_scripts.utils import find_binary, run + + +def eid_xor( + error_pattern: Union[str, Path], +): + # TODO + binary = find_binary("eid-xor") + + # check for valid inputs + + # set up command line + + # run command + # result = run(cmd) + + return + + +def apply_error_pattern(): + return diff --git a/ivas_processing_scripts/audiotools/wrappers/gen-patt.py b/ivas_processing_scripts/audiotools/wrappers/gen-patt.py new file mode 100644 index 00000000..ef9b6a19 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/gen-patt.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 warnings +from pathlib import Path +from ivas_processing_scripts.utils import find_binary, run + + +LIST_FER = [3, 5, 6, 10] + + +def gen_patt(): + # TODO: call binary + binary = find_binary("eid-xor") + + # run command + # result = run(cmd) + return + + +def create_error_pattern( + frame_error_rate: float, + preamble: int, +): + # TODO: copy sta files to cwd and call gen_patt + # TODO: start after preamble (if preamble is int multiple of frame length) + if frame_error_rate in LIST_FER: + # copy sta file + pass + else: + warnings.warn(f"Frame error rate {frame_error_rate}% is not a default rate. Error pattern is random and not deterministic.") + + return diff --git a/ivas_processing_scripts/error_patterns/sta_03 b/ivas_processing_scripts/error_patterns/sta_03 new file mode 100644 index 00000000..c2f2c35e --- /dev/null +++ b/ivas_processing_scripts/error_patterns/sta_03 @@ -0,0 +1,9 @@ +EID +BER = 0.030000 +GAMMA = 0.000000 +RAN-seed = 0xab5d4825 +Current State = G +GOOD->GOOD = 0.940000 +GOOD->BAD = 1.000000 +BAD ->GOOD = 0.940000 +BAD ->BAD = 1.000000 \ No newline at end of file diff --git a/ivas_processing_scripts/error_patterns/sta_05 b/ivas_processing_scripts/error_patterns/sta_05 new file mode 100644 index 00000000..c872f3a6 --- /dev/null +++ b/ivas_processing_scripts/error_patterns/sta_05 @@ -0,0 +1,9 @@ +EID +BER = 0.050000 +GAMMA = 0.000000 +RAN-seed = 0x6d30b654 +Current State = G +GOOD->GOOD = 0.900000 +GOOD->BAD = 1.000000 +BAD ->GOOD = 0.900000 +BAD ->BAD = 1.000000 diff --git a/ivas_processing_scripts/error_patterns/sta_06 b/ivas_processing_scripts/error_patterns/sta_06 new file mode 100644 index 00000000..55e17ec2 --- /dev/null +++ b/ivas_processing_scripts/error_patterns/sta_06 @@ -0,0 +1,9 @@ +EID +BER = 0.060000 +GAMMA = 0.000000 +RAN-seed = 0x176d71ac +Current State = G +GOOD->GOOD = 0.880000 +GOOD->BAD = 1.000000 +BAD ->GOOD = 0.880000 +BAD ->BAD = 1.000000 \ No newline at end of file diff --git a/ivas_processing_scripts/error_patterns/sta_10 b/ivas_processing_scripts/error_patterns/sta_10 new file mode 100644 index 00000000..cf8517ae --- /dev/null +++ b/ivas_processing_scripts/error_patterns/sta_10 @@ -0,0 +1,9 @@ +EID +BER = 0.100000 +GAMMA = 0.000000 +RAN-seed = 0x356007f8 +Current State = B +GOOD->GOOD = 0.800000 +GOOD->BAD = 1.000000 +BAD ->GOOD = 0.800000 +BAD ->BAD = 1.000000 -- GitLab From a6abb38d88cd27e7a00df2a6d1ed8ac8f864a3d9 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Mon, 17 Apr 2023 18:01:20 +0200 Subject: [PATCH 11/31] first version bitstream processing with error pattern --- examples/TEMPLATE.yml | 18 +- .../audiotools/wrappers/bs1770.py | 2 +- .../audiotools/wrappers/ei-xor.py | 56 ------ .../audiotools/wrappers/eid_xor.py | 136 +++++++++++++++ .../audiotools/wrappers/gen-patt.py | 62 ------- .../audiotools/wrappers/gen_patt.py | 163 ++++++++++++++++++ .../error_patterns/{sta_03 => sta_3} | 0 .../error_patterns/{sta_05 => sta_5} | 0 .../error_patterns/{sta_06 => sta_6} | 0 ivas_processing_scripts/processing/chains.py | 37 ++-- ivas_processing_scripts/processing/ivas.py | 51 ++++-- 11 files changed, 373 insertions(+), 152 deletions(-) delete mode 100644 ivas_processing_scripts/audiotools/wrappers/ei-xor.py create mode 100644 ivas_processing_scripts/audiotools/wrappers/eid_xor.py delete mode 100644 ivas_processing_scripts/audiotools/wrappers/gen-patt.py create mode 100644 ivas_processing_scripts/audiotools/wrappers/gen_patt.py rename ivas_processing_scripts/error_patterns/{sta_03 => sta_3} (100%) rename ivas_processing_scripts/error_patterns/{sta_05 => sta_5} (100%) rename ivas_processing_scripts/error_patterns/{sta_06 => sta_6} (100%) diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index c63114e3..97258d39 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -94,15 +94,21 @@ input: ################################################# ### Bistream processing (transport simulation) done after encoding and before decoding ### e.g. frame error insertion or transport simulation for JBM testing -# tx: +# tx_jbm: ### REQUIRED: Path to network simulation binary # bs_proc_bin: ".../ivas_python_testscripts/networkSimulator_g192.exe" - ### Path to error pattern (mandatory if no information for generating the error pattern is given) - # error_pattern: ".../dly_error_profile.dat" - ### options for the binary, possible placeholders are {error_pattern} for the error pattern, + ### Path to delay error profile (mandatory if no information for generating the error pattern is given) + # delay_error_profile: ".../dly_error_profile.dat" + ### options for the binary, possible placeholders are {delay_error_profile} for the error pattern, ### {bitstream} for the bitstream to process and {bitstream_processed} for the processed bitstream - # bs_proc_opts: [ "{error_pattern}", "{bitstream}", "{processed_bitstream}", "{processed_bitstream}_tracefile_sim", "2", "0" ] - + # bs_proc_opts: [ "{delay_error_profile}", "{bitstream}", "{processed_bitstream}", "{processed_bitstream}_tracefile_sim", "2", "0" ] +# tx_fer: + ### REQUIRED: either error_pattern or error_rate + ### Frame error pattern file + # error_pattern: "path/pattern.192" + ### Error rate in percent + # error_rate: 5 + ################################################ ### Configuration for conditions under test ################################################ diff --git a/ivas_processing_scripts/audiotools/wrappers/bs1770.py b/ivas_processing_scripts/audiotools/wrappers/bs1770.py index 4011d3eb..a047c339 100755 --- a/ivas_processing_scripts/audiotools/wrappers/bs1770.py +++ b/ivas_processing_scripts/audiotools/wrappers/bs1770.py @@ -57,7 +57,7 @@ def bs1770demo( Parameters ---------- - input : Audio + input: Audio Input audio target_loudness: Optional[float] Desired loudness in LKFS diff --git a/ivas_processing_scripts/audiotools/wrappers/ei-xor.py b/ivas_processing_scripts/audiotools/wrappers/ei-xor.py deleted file mode 100644 index c6627332..00000000 --- a/ivas_processing_scripts/audiotools/wrappers/ei-xor.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -# -# (C) 2022-2023 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 -from typing import Union - -from ivas_processing_scripts.utils import find_binary, run - - -def eid_xor( - error_pattern: Union[str, Path], -): - # TODO - binary = find_binary("eid-xor") - - # check for valid inputs - - # set up command line - - # run command - # result = run(cmd) - - return - - -def apply_error_pattern(): - return diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py new file mode 100644 index 00000000..74a6a6b7 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 +from typing import Optional, Union + +from ivas_processing_scripts.audiotools.wrappers.gen_patt import create_error_pattern +from ivas_processing_scripts.utils import find_binary, run + + +def eid_xor( + error_pattern: Union[str, Path], + in_bitstream: Union[str, Path], + out_bitstream: Union[str, Path], +) -> None: + """ + Wrapper for eid-xor binary to apply error patterns for the bitstream processing + + Parameters + ---------- + error_pattern: Union[str, Path] + Path to error pattern file + in_bitstream: Union[str, Path] + Path to input bitstream file + out_bitstream: Union[str, Path] + Output path for modified bitstream + """ + + # find binary + binary = find_binary("eid-xor") + + # check for valid inputs + if not Path(in_bitstream).is_file(): + raise ValueError( + f"Input bitstream file {in_bitstream} for bitstream processing does not exist" + ) + elif not Path(error_pattern).is_file(): + raise ValueError( + f"Error pattern file {error_pattern} for bitstream processing does not exist" + ) + + # set up command line + cmd = [ + str(binary), + "-vbr", # Enables variable bit rate operation + "-fer", # Error pattern is a frame erasure pattern + in_bitstream, + error_pattern, + out_bitstream, + ] + + # run command + run(cmd) + + return + + +def create_and_apply_error_pattern( + in_bitstream: Union[Path, str], + out_bitstream: Union[Path, str], + len_sig: int, + error_pattern: Optional[Union[Path, str]] = None, + error_rate: Optional[float] = None, + preamble: Optional[int] = 0, + fs: Optional[int] = 48000, +) -> None: + """ + Function to create (or use existing) frame error pattern for bitstream processing + + Parameters + ---------- + in_bitstream: Union[Path, str] + Path of input bitstream + out_bitstream: Union[Path, str] + Path of output bitstream + len_sig: int + Length of signal in samples + error_pattern: Optional[Union[Path, str]] + Path to existing error pattern + error_rate: float + Error rate in percent + preamble: Optional[int] + Length of preamble + fs: Optional[int] + Sampling rate + """ + + if error_pattern is None: + # create error pattern + if error_rate is not None: + error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix( + ".192" + ) + create_error_pattern(len_sig, error_pattern, error_rate, preamble, fs) + else: + raise ValueError( + "Either error pattern or error rate has to be specified for bitstream processing" + ) + elif error_rate is not None: + raise ValueError( + "Error pattern and error rate are specified for bitstream processing. Can't use both" + ) + + # apply error pattern + eid_xor(error_pattern, in_bitstream, out_bitstream) + + return diff --git a/ivas_processing_scripts/audiotools/wrappers/gen-patt.py b/ivas_processing_scripts/audiotools/wrappers/gen-patt.py deleted file mode 100644 index ef9b6a19..00000000 --- a/ivas_processing_scripts/audiotools/wrappers/gen-patt.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -# -# (C) 2022-2023 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 warnings -from pathlib import Path -from ivas_processing_scripts.utils import find_binary, run - - -LIST_FER = [3, 5, 6, 10] - - -def gen_patt(): - # TODO: call binary - binary = find_binary("eid-xor") - - # run command - # result = run(cmd) - return - - -def create_error_pattern( - frame_error_rate: float, - preamble: int, -): - # TODO: copy sta files to cwd and call gen_patt - # TODO: start after preamble (if preamble is int multiple of frame length) - if frame_error_rate in LIST_FER: - # copy sta file - pass - else: - warnings.warn(f"Frame error rate {frame_error_rate}% is not a default rate. Error pattern is random and not deterministic.") - - return diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py new file mode 100644 index 00000000..c2256da8 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 shutil +import warnings +from os import getcwd +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Optional, Union + +from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS +from ivas_processing_scripts.utils import find_binary, run + +LIST_FER = [3, 5, 6, 10] +ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("error_patterns") + + +def gen_patt( + len_sig: int, + path_pattern: Union[Path, str], + error_rate: float, + start: Optional[int] = 0, + fs: Optional[int] = 48000, + working_dir: Optional[Union[Path, str]] = None, +) -> None: + """ + Wrapper for gen-patt binary to create error patterns for the bitstream processing + + Parameters + ---------- + len_sig: int + Length of signal in samples + path_pattern: Union[Path, str] + Path of output pattern + error_rate: float + Error rate in percent + start: Optional[int] + Start sample of error pattern (length preamble) + fs: Optional[int] + Sampling rate + working_dir: Optional[Union[Path, str]] + Directory where binary should be called (sta file has to be in this dir if desired) + """ + + # check if multiple of frame length + if len_sig % int(IVAS_FRAME_LEN_MS * fs // 1000): + raise ValueError( + "Signal length has to be integer multiple of frame length for bitstream processing" + ) + if start % int(IVAS_FRAME_LEN_MS * fs // 1000): + raise ValueError( + "Preamble length has to be integer multiple of frame length for bitstream processing" + ) + + # find binary + binary = find_binary("gen-patt") + + if working_dir is None: + working_dir = getcwd() + + # set up command line + cmd = [ + str(binary), + "-tailstat", # Statistics performed on the tail + "-fer", # Frame erasure mode using Gilbert model + "-g192", # Save error pattern in 16-bit G.192 format + "-gamma", # Correlation for BER|FER modes + str(0), + "-rate", + str(error_rate / 100), + "-tol", # Max deviation of specified BER/FER/BFER + str(0.001), + "-reset", # Reset EID state in between iteractions + "-n", + str(int(len_sig / int(IVAS_FRAME_LEN_MS * fs // 1000))), + "-start", + str(int(start / int(IVAS_FRAME_LEN_MS * fs // 1000)) + 1), + path_pattern, + ] + + # run command + run(cmd, cwd=working_dir) + + return + + +def create_error_pattern( + len_sig: int, + path_pattern: Union[Path, str], + frame_error_rate: float, + preamble: Optional[int] = 0, + fs: Optional[int] = 48000, +) -> None: + """ + Creates error pattern with desired frame error rate for bitstream processing + For 3, 5, 6 and 10 percent FER the results are deterministic + + Parameters + ---------- + len_sig: int + Length of signal in samples + path_pattern: Union[Path, str] + Path of output pattern + frame_error_rate: float + Error rate in percent + preamble: Optional[int] + Length of preamble + fs: Optional[int] + Sampling rate + """ + + with TemporaryDirectory() as tmp_dir: + tmp_dir = Path(tmp_dir) + if frame_error_rate in LIST_FER: + # copy sta file + sta_file = ERROR_PATTERNS_DIR.joinpath(f"sta_{int(frame_error_rate)}") + tmp_sta_file = tmp_dir.joinpath("sta") + shutil.copyfile(sta_file, tmp_sta_file) + + else: + warnings.warn( + f"Frame error rate {frame_error_rate}% is not a default rate. Error pattern is random and not deterministic." + ) + + gen_patt( + len_sig=len_sig, + error_rate=frame_error_rate, + path_pattern=path_pattern, + start=preamble, + fs=fs, + working_dir=tmp_dir, + ) + + return diff --git a/ivas_processing_scripts/error_patterns/sta_03 b/ivas_processing_scripts/error_patterns/sta_3 similarity index 100% rename from ivas_processing_scripts/error_patterns/sta_03 rename to ivas_processing_scripts/error_patterns/sta_3 diff --git a/ivas_processing_scripts/error_patterns/sta_05 b/ivas_processing_scripts/error_patterns/sta_5 similarity index 100% rename from ivas_processing_scripts/error_patterns/sta_05 rename to ivas_processing_scripts/error_patterns/sta_5 diff --git a/ivas_processing_scripts/error_patterns/sta_06 b/ivas_processing_scripts/error_patterns/sta_6 similarity index 100% rename from ivas_processing_scripts/error_patterns/sta_06 rename to ivas_processing_scripts/error_patterns/sta_6 diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index f9edbc36..d38a55fb 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -211,17 +211,33 @@ def get_processing_chain( cod_cfg = cond_cfg["cod"] dec_cfg = cond_cfg["dec"] - # TODO: local tx overrides global one, or just allow global? - if "tx" in cond_cfg.keys(): - tx_cfg = cond_cfg["tx"] - elif hasattr(cfg, "tx"): - tx_cfg = { - "bin": cfg.tx["bs_proc_bin"], - "error_pattern": cfg.tx["error_pattern"], - "opts": cfg.tx["bs_proc_opts"], + # Frame error pattern bitstream modification + if "tx_fer" in cond_cfg.keys(): + tx_fer_cfg = cond_cfg["tx_fer"] + elif hasattr(cfg, "tx_fer"): + tx_fer_cfg = { + "error_pattern": cfg.tx_fer.get("error_pattern", None), + "error_rate": cfg.tx_fer.get("error_rate", None), } else: - tx_cfg = None + tx_fer_cfg = None + + # JBM delay error profile bitstream modification + if "tx_jmb" in cond_cfg.keys(): + tx_jbm_cfg = cond_cfg["tx_jbm"] + elif hasattr(cfg, "tx_jbm"): + tx_jbm_cfg = { + "bin": cfg.tx_jbm["bs_proc_bin"], + "delay_error_profile": cfg.tx_jbm["delay_error_profile"], + "opts": cfg.tx_jbm["bs_proc_opts"], + } + else: + tx_jbm_cfg = None + + if tx_fer_cfg is not None and tx_jbm_cfg is not None: + raise ValueError( + "Application of error pattern and delay error profile simultaneously is not possible" + ) chain["processes"].append( IVAS( @@ -236,7 +252,8 @@ def get_processing_chain( "dec_bin": dec_cfg.get("bin"), "dec_opts": dec_cfg.get("opts"), "multiprocessing": cfg.multiprocessing, - "tx": tx_cfg, + "tx_jbm": tx_jbm_cfg, + "tx_fer": tx_fer_cfg, } ) ) diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index d7709360..dda2636d 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -35,10 +35,13 @@ import os.path import platform from copy import deepcopy from pathlib import Path -from typing import Optional +from typing import Optional, Union from ivas_processing_scripts.audiotools import audio -from ivas_processing_scripts.audiotools.audiofile import parse_wave_header +from ivas_processing_scripts.audiotools.audiofile import parse_wave_header, read +from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( + create_and_apply_error_pattern, +) from ivas_processing_scripts.processing.processing import Processing from ivas_processing_scripts.utils import run @@ -70,12 +73,6 @@ class IVAS(Processing): raise FileNotFoundError( "The IVAS decoder binary was not found! Please check the configuration." ) - if self.tx is not None: - if not self.tx["bin"] or not Path(self.tx["bin"]).exists(): - bin = self.tx["bin"] - raise FileNotFoundError( - f"The transport simulator binary {bin} was not found! Please check the configuration." - ) def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger @@ -100,7 +97,7 @@ class IVAS(Processing): self.enc(in_file, bitstream, in_meta, logger) - bitstream = self.simulate_tx(bitstream, logger) + bitstream = self.simulate_tx(in_file, bitstream, logger) self.dec(bitstream, out_file, logger) @@ -191,15 +188,17 @@ class IVAS(Processing): run(cmd, logger=logger) def simulate_tx( - self, bitstream: Path, logger: Optional[logging.Logger] = None - ) -> Path: - # TODO run eid-xor/networkSimulator - if self.tx is not None: - cmd = [self.tx["bin"]] + self, + in_file: Union[Path, str], + bitstream: Path, + logger: Optional[logging.Logger] = None, + ) -> Union[Path, str]: + if self.tx_jbm is not None: + cmd = [self.tx_jbm["bin"]] bs, ext = os.path.splitext(bitstream) - error_pattern = self.tx["error_pattern"] + error_pattern = self.tx_jbm["error_pattern"] bitstream_processed = Path(bs + "." + os.path.basename(error_pattern) + ext) - opts = deepcopy(self.tx["opts"]) + opts = deepcopy(self.tx_jbm["opts"]) opts = [ x.format( error_pattern=error_pattern, @@ -208,10 +207,28 @@ class IVAS(Processing): ) for x in opts ] - logger.debug(f"Network simulator {bitstream} -> {bitstream_processed}") + logger.debug(f"JBM network simulator {bitstream} -> {bitstream_processed}") cmd.extend(opts) run(cmd, logger=logger) return bitstream_processed + + elif self.tx_fer is not None: + bs, ext = os.path.splitext(bitstream) + bitstream_processed = Path(f"{bs}_processed{ext}") + signal, _ = read( + in_file, fs=self.in_fs + ) # TODO: pass down number of frames and preamble from concatenation + logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") + create_and_apply_error_pattern( + bitstream, + bitstream_processed, + len(signal), + self.tx_fer["error_pattern"], + self.tx_fer["error_rate"], + fs=self.in_fs, + ) + + return bitstream_processed else: return bitstream -- GitLab From cc99092711f90af3ddd9abecc80c3eb72af1d289 Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 09:06:26 +0200 Subject: [PATCH 12/31] added wrapper for the network simulator --- .../audiotools/wrappers/networkSimulator.py | 169 ++++++++++++++++++ ivas_processing_scripts/processing/chains.py | 6 +- ivas_processing_scripts/processing/ivas.py | 41 +++-- 3 files changed, 198 insertions(+), 18 deletions(-) create mode 100644 ivas_processing_scripts/audiotools/wrappers/networkSimulator.py diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py new file mode 100644 index 00000000..e5ff3994 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 +from typing import Optional, Union +import os.path + +from ivas_processing_scripts.audiotools.wrappers.gen_patt import create_error_pattern +from ivas_processing_scripts.utils import find_binary, run + +LIST_JBM_PROFILES = range(12) +ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("dly_error_profiles") + + +def validate_network_simulator( + error_pattern: Optional[Union[Path, str]] = None, + error_profile: Optional[int] = None, + ): + if find_binary("networkSimulator_g192")is None: + raise FileNotFoundError( + "The network simulator binary was not found! Please check the configuration." + ) + if error_pattern is not None: + if not os.path.exists(os.path.realpath(error_pattern)): + raise FileNotFoundError( + f"The network simulator error profile file {error_pattern} was not found! Please check the configuration." + ) + if error_profile is not None: + raise ValueError( + "JBM pattern and JBM profile number are specified for bitstream processing. Can't use both! Please check the configuration." + ) + elif error_profile is not None: + if error_profile not in LIST_JBM_PROFILES: + raise ValueError( + f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" + ) + + +def network_simulator( + error_pattern: Union[str, Path], + in_bitstream: Union[str, Path], + out_bitstream: Union[str, Path], + n_frames_per_paket: int, +) -> None: + """ + Wrapper for eid-xor binary to apply error patterns for the bitstream processing + + Parameters + ---------- + error_pattern: Union[str, Path] + Path to error pattern file + in_bitstream: Union[str, Path] + Path to input bitstream file + out_bitstream: Union[str, Path] + Output path for modified bitstream + """ + + # find binary + binary = find_binary("networkSimulator_g192") + + # check for valid inputs + if not Path(in_bitstream).is_file(): + raise ValueError( + f"Input bitstream file {in_bitstream} for bitstream processing does not exist" + ) + elif not Path(error_pattern).is_file(): + raise ValueError( + f"Error pattern file {error_pattern} for bitstream processing does not exist" + ) + + # set up command line + cmd = [ + str(binary), + error_pattern, + in_bitstream, + out_bitstream, + f"{out_bitstream}_tracefile_sim", + str(n_frames_per_paket), + "0" + ] + + # run command + run(cmd) + + return + + +def apply_network_simulator( + in_bitstream: Union[Path, str], + out_bitstream: Union[Path, str], + error_pattern: Optional[Union[Path, str]] = None, + error_profile: Optional[int] = None, + n_frames_per_paket: Optional[int] = None, +) -> None: + """ + Function to apply a jbm error profile to a bitstreaam + + Parameters + ---------- + in_bitstream: Union[Path, str] + Path of input bitstream + out_bitstream: Union[Path, str] + Path of output bitstream + error_pattern: Optional[Union[Path, str]] + Path to existing error pattern + error_profile: Optional[int] + Index of existing error pattern + n_frames_per_paket: Optional[int] + number of frames per paket + fs: Optional[int] + Sampling rate + """ + + if error_pattern is None: + # create error pattern + if error_profile is not None: + if error_profile in LIST_JBM_PROFILES: + error_pattern = ERROR_PATTERNS_DIR.joinpath(f"dly_error_profile_{error_profile}.dat") + else: + raise ValueError( + f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" + ) + else: + raise ValueError( + "Either error pattern or error profile number has to be specified for network simulator bitstream processing" + ) + elif error_profile is not None: + raise ValueError( + "JBM pattern and JBM profile number are specified for bitstream processing. Can't use both" + ) + + if n_frames_per_paket is None: + n_frames_per_paket = 1 + if error_profile is not None and error_profile == 5: + n_frames_per_paket = 2 + + # apply error pattern + network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_paket) + + return diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index d38a55fb..c4615834 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -227,9 +227,9 @@ def get_processing_chain( tx_jbm_cfg = cond_cfg["tx_jbm"] elif hasattr(cfg, "tx_jbm"): tx_jbm_cfg = { - "bin": cfg.tx_jbm["bs_proc_bin"], - "delay_error_profile": cfg.tx_jbm["delay_error_profile"], - "opts": cfg.tx_jbm["bs_proc_opts"], + "error_pattern": cfg.tx_jbm.get("error_pattern", None), + "error_profile": cfg.tx_jbm.get("error_profile", None), + "n_frames_per_paket": cfg.tx_jbm.get("n_frames_per_paket", 1), } else: tx_jbm_cfg = None diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index dda2636d..ea14ae0d 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -41,6 +41,8 @@ from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import parse_wave_header, read from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( create_and_apply_error_pattern, +)from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( + apply_network_simulator, validate_network_simulator ) from ivas_processing_scripts.processing.processing import Processing from ivas_processing_scripts.utils import run @@ -53,6 +55,8 @@ class IVAS(Processing): self.name = "ivas" self.in_fmt = audio.fromtype(self.in_fmt) self.out_fmt = audio.fromtype(self.out_fmt) + if not hasattr(self,"dec_opts"): + self.dec_opts = None def _validate(self): if not self.cod_bin or not Path(self.cod_bin).exists(): @@ -73,6 +77,11 @@ class IVAS(Processing): raise FileNotFoundError( "The IVAS decoder binary was not found! Please check the configuration." ) + # TODO: if tx_fer or tx_jbm test for existence of bitstream processing binaries and + # existence of error pattern files (if given) already here + if self.tx_jbm is not None: + validate_network_simulator(self.tx_jbm["error_pattern"],self.tx_jbm["error_profile"]) + def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger @@ -194,22 +203,24 @@ class IVAS(Processing): logger: Optional[logging.Logger] = None, ) -> Union[Path, str]: if self.tx_jbm is not None: - cmd = [self.tx_jbm["bin"]] bs, ext = os.path.splitext(bitstream) - error_pattern = self.tx_jbm["error_pattern"] - bitstream_processed = Path(bs + "." + os.path.basename(error_pattern) + ext) - opts = deepcopy(self.tx_jbm["opts"]) - opts = [ - x.format( - error_pattern=error_pattern, - bitstream=bitstream, - processed_bitstream=bitstream_processed, - ) - for x in opts - ] - logger.debug(f"JBM network simulator {bitstream} -> {bitstream_processed}") - cmd.extend(opts) - run(cmd, logger=logger) + bitstream_processed = Path(f"{bs}_processed{ext}") + logger.debug(f"Network simulator {bitstream} -> {bitstream_processed}") + apply_network_simulator( + bitstream, + bitstream_processed, + self.tx_jbm["error_pattern"], + self.tx_jbm["error_profile"], + self.tx_jbm["n_frames_per_paket"], + ) + # add -voip cmdline option to the decoder + # TODO: tracefile also? + if self.dec_opts: + if "-voip" not in self.dec_opts: + self.dec_opts.extend(["-voip"]) + + else: + self.dec_opts = ["-voip"] return bitstream_processed elif self.tx_fer is not None: -- GitLab From 131e8d92f7cb395c053f0936a6035cdad039719d Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 09:09:33 +0200 Subject: [PATCH 13/31] added JBM daily error profiles --- .../dly_error_profiles/dly_error_profile_0.dat | 3 +++ .../dly_error_profiles/dly_error_profile_1.dat | 3 +++ .../dly_error_profiles/dly_error_profile_10.dat | 3 +++ .../dly_error_profiles/dly_error_profile_11.dat | 3 +++ .../dly_error_profiles/dly_error_profile_2.dat | 3 +++ .../dly_error_profiles/dly_error_profile_3.dat | 3 +++ .../dly_error_profiles/dly_error_profile_4.dat | 3 +++ .../dly_error_profiles/dly_error_profile_5.dat | 3 +++ .../dly_error_profiles/dly_error_profile_6.dat | 3 +++ .../dly_error_profiles/dly_error_profile_7.dat | 3 +++ .../dly_error_profiles/dly_error_profile_8.dat | 3 +++ .../dly_error_profiles/dly_error_profile_9.dat | 3 +++ 12 files changed, 36 insertions(+) create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_0.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_1.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_10.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_11.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_2.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_3.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_4.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_5.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_6.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_7.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_8.dat create mode 100644 ivas_processing_scripts/dly_error_profiles/dly_error_profile_9.dat diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_0.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_0.dat new file mode 100644 index 00000000..804fcc90 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_0.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa +size 2 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_1.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_1.dat new file mode 100644 index 00000000..cb7a2259 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_1.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3c23b360bd99f9d56d4c39912af8f399b4b3809bb23beecf9fe7be3ac162d72 +size 30000 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_10.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_10.dat new file mode 100644 index 00000000..c6d88aa9 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_10.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe10c959f17a185a272fae419b328de835981f4e845b7ac581b44dac6b9833f8 +size 31247 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_11.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_11.dat new file mode 100644 index 00000000..7553ab39 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_11.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:950ba178f3cacaeec6c3dd97e651177c9768290770edcc998ab7b8925722b147 +size 71025 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_2.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_2.dat new file mode 100644 index 00000000..6bdf6557 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_2.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:400ab310d7d5d9aef139939fbe1130c510d275255394ab1e15b32162034ad704 +size 29982 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_3.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_3.dat new file mode 100644 index 00000000..1668e4e3 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_3.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:589722d6abd82ce5bdec9e5a57ac37011fa6385de9ad74a6df660bdc8b2f7b05 +size 29962 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_4.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_4.dat new file mode 100644 index 00000000..3fafe8e5 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_4.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0db043e25d133b68488f3a815f2f438c97a773f287c1cc50281dfcc3a38c1ea1 +size 29820 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_5.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_5.dat new file mode 100644 index 00000000..cd00cd9e --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_5.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff5fc111d516dfa335e85c381b04c229ba40576099af7ae079559d49147f23fd +size 29556 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_6.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_6.dat new file mode 100644 index 00000000..940ad87c --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_6.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d661669942ee60dc6a2bd83e2e05e3daea06149928d350eca97a72868fd9c522 +size 30000 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_7.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_7.dat new file mode 100644 index 00000000..ab1e94f6 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_7.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd1f5a09a5fef9aacf7b4b030af813c982ce117eb0ff79008e248d575a146824 +size 31737 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_8.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_8.dat new file mode 100644 index 00000000..c0369c37 --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_8.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0de3fd89270f89f4d134344b57925f3b7a115dd581cfa9d36ecb26d39cf4b76f +size 31503 diff --git a/ivas_processing_scripts/dly_error_profiles/dly_error_profile_9.dat b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_9.dat new file mode 100644 index 00000000..ca8384de --- /dev/null +++ b/ivas_processing_scripts/dly_error_profiles/dly_error_profile_9.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f0eed7aa93fe00fe88595a95c25a4ba250298df73159a42bc53cfcafeefa294 +size 31343 -- GitLab From 5381a15e9a478bc8837572f6d4993e586b2e9560 Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 09:16:36 +0200 Subject: [PATCH 14/31] added another sanity check for the network simulator --- .../audiotools/wrappers/networkSimulator.py | 10 ++++++++-- ivas_processing_scripts/processing/ivas.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index e5ff3994..b0a05bd2 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -44,6 +44,7 @@ ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("dly_error_pro def validate_network_simulator( error_pattern: Optional[Union[Path, str]] = None, error_profile: Optional[int] = None, + n_frames_per_paket: Optional[int] = None, ): if find_binary("networkSimulator_g192")is None: raise FileNotFoundError( @@ -63,7 +64,10 @@ def validate_network_simulator( raise ValueError( f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" ) - + if n_frames_per_paket is not None and n_frames_per_paket not in [1,2]: + raise ValueError( + f"n_frames_per_paket is {n_frames_per_paket}. Should be 1 or 2. Please check your configuration." + ) def network_simulator( error_pattern: Union[str, Path], @@ -82,6 +86,8 @@ def network_simulator( Path to input bitstream file out_bitstream: Union[str, Path] Output path for modified bitstream + n_frames_per_paket: int, + number of frames per paket [1,2] """ # find binary @@ -122,7 +128,7 @@ def apply_network_simulator( n_frames_per_paket: Optional[int] = None, ) -> None: """ - Function to apply a jbm error profile to a bitstreaam + Function to apply a network simulator profile to a bitstreaam Parameters ---------- diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index ea14ae0d..2bc9220b 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -80,7 +80,7 @@ class IVAS(Processing): # TODO: if tx_fer or tx_jbm test for existence of bitstream processing binaries and # existence of error pattern files (if given) already here if self.tx_jbm is not None: - validate_network_simulator(self.tx_jbm["error_pattern"],self.tx_jbm["error_profile"]) + validate_network_simulator(self.tx_jbm["error_pattern"],self.tx_jbm["error_profile"], self.tx_jbm["n_frames_per_paket"]) def process( -- GitLab From bfbf367fd437752bf89ed239b891a945dc503e3b Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 09:21:39 +0200 Subject: [PATCH 15/31] fix syntax error --- ivas_processing_scripts/processing/ivas.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 2bc9220b..fd4bc202 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -41,7 +41,8 @@ from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import parse_wave_header, read from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( create_and_apply_error_pattern, -)from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( +) +from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( apply_network_simulator, validate_network_simulator ) from ivas_processing_scripts.processing.processing import Processing -- GitLab From 5e014d8633bade671ebc02956d28d73ef3e0b771 Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 09:29:27 +0200 Subject: [PATCH 16/31] small code beautification --- .../audiotools/wrappers/networkSimulator.py | 20 ++++++++++++++++--- ivas_processing_scripts/processing/ivas.py | 5 +++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index b0a05bd2..b7436a30 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -45,8 +45,19 @@ def validate_network_simulator( error_pattern: Optional[Union[Path, str]] = None, error_profile: Optional[int] = None, n_frames_per_paket: Optional[int] = None, - ): - if find_binary("networkSimulator_g192")is None: +) -> None: + """Validate settings for the network simulator + + Parameters + ---------- + error_pattern: Optional[Union[Path, str]] + Path to existing error pattern + error_profile: Optional[int] + Index of existing error pattern + n_frames_per_paket: Optional[int] + number of frames per paket + """ + if find_binary("networkSimulator_g192") is None: raise FileNotFoundError( "The network simulator binary was not found! Please check the configuration." ) @@ -64,11 +75,14 @@ def validate_network_simulator( raise ValueError( f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" ) - if n_frames_per_paket is not None and n_frames_per_paket not in [1,2]: + if n_frames_per_paket is not None and n_frames_per_paket not in [1, 2]: raise ValueError( f"n_frames_per_paket is {n_frames_per_paket}. Should be 1 or 2. Please check your configuration." ) + return + + def network_simulator( error_pattern: Union[str, Path], in_bitstream: Union[str, Path], diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index fd4bc202..fcc8ab1e 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -81,8 +81,9 @@ class IVAS(Processing): # TODO: if tx_fer or tx_jbm test for existence of bitstream processing binaries and # existence of error pattern files (if given) already here if self.tx_jbm is not None: - validate_network_simulator(self.tx_jbm["error_pattern"],self.tx_jbm["error_profile"], self.tx_jbm["n_frames_per_paket"]) - + validate_network_simulator(self.tx_jbm["error_pattern"], + self.tx_jbm["error_profile"], + self.tx_jbm["n_frames_per_paket"]) def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger -- GitLab From a5261395ea8fc5db972febefa922aa1d6b6ff35f Mon Sep 17 00:00:00 2001 From: Treffehn Date: Tue, 18 Apr 2023 13:57:58 +0200 Subject: [PATCH 17/31] added type to bitstream processing --- examples/TEMPLATE.yml | 22 +++--- .../audiotools/wrappers/eid_xor.py | 9 +-- .../audiotools/wrappers/gen_patt.py | 29 ++------ .../audiotools/wrappers/networkSimulator.py | 22 +++--- ivas_processing_scripts/processing/chains.py | 47 +++++------- ivas_processing_scripts/processing/ivas.py | 74 +++++++++---------- 6 files changed, 90 insertions(+), 113 deletions(-) diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 97258d39..83b56ae2 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -94,15 +94,19 @@ input: ################################################# ### Bistream processing (transport simulation) done after encoding and before decoding ### e.g. frame error insertion or transport simulation for JBM testing -# tx_jbm: - ### REQUIRED: Path to network simulation binary - # bs_proc_bin: ".../ivas_python_testscripts/networkSimulator_g192.exe" - ### Path to delay error profile (mandatory if no information for generating the error pattern is given) - # delay_error_profile: ".../dly_error_profile.dat" - ### options for the binary, possible placeholders are {delay_error_profile} for the error pattern, - ### {bitstream} for the bitstream to process and {bitstream_processed} for the processed bitstream - # bs_proc_opts: [ "{delay_error_profile}", "{bitstream}", "{processed_bitstream}", "{processed_bitstream}_tracefile_sim", "2", "0" ] -# tx_fer: +# tx: + ### REQUIRED: Type of bitsream processing; possible types: "jbm" or "fer" + #type: "jbm" + + ### JBM + ### Path to delay error pattern (mandatory if no information for generating the error pattern is given) + # error_pattern: ".../dly_error_profile.dat" + ## + # error_profile: + ## ; defalt = 1 + # n_frames_per_paket: 2 + + ### FER ### REQUIRED: either error_pattern or error_rate ### Frame error pattern file # error_pattern: "path/pattern.192" diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py index 74a6a6b7..c9141ff2 100644 --- a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -91,7 +91,6 @@ def create_and_apply_error_pattern( error_pattern: Optional[Union[Path, str]] = None, error_rate: Optional[float] = None, preamble: Optional[int] = 0, - fs: Optional[int] = 48000, ) -> None: """ Function to create (or use existing) frame error pattern for bitstream processing @@ -103,15 +102,13 @@ def create_and_apply_error_pattern( out_bitstream: Union[Path, str] Path of output bitstream len_sig: int - Length of signal in samples + Length of signal in frames error_pattern: Optional[Union[Path, str]] Path to existing error pattern error_rate: float Error rate in percent preamble: Optional[int] - Length of preamble - fs: Optional[int] - Sampling rate + Length of preamble in frames """ if error_pattern is None: @@ -120,7 +117,7 @@ def create_and_apply_error_pattern( error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix( ".192" ) - create_error_pattern(len_sig, error_pattern, error_rate, preamble, fs) + create_error_pattern(len_sig, error_pattern, error_rate, preamble) else: raise ValueError( "Either error pattern or error rate has to be specified for bitstream processing" diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index c2256da8..72b565f3 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -49,7 +49,6 @@ def gen_patt( path_pattern: Union[Path, str], error_rate: float, start: Optional[int] = 0, - fs: Optional[int] = 48000, working_dir: Optional[Union[Path, str]] = None, ) -> None: """ @@ -58,29 +57,17 @@ def gen_patt( Parameters ---------- len_sig: int - Length of signal in samples + Length of signal in frames path_pattern: Union[Path, str] Path of output pattern error_rate: float Error rate in percent start: Optional[int] - Start sample of error pattern (length preamble) - fs: Optional[int] - Sampling rate + Start frame of error pattern (length preamble) working_dir: Optional[Union[Path, str]] Directory where binary should be called (sta file has to be in this dir if desired) """ - # check if multiple of frame length - if len_sig % int(IVAS_FRAME_LEN_MS * fs // 1000): - raise ValueError( - "Signal length has to be integer multiple of frame length for bitstream processing" - ) - if start % int(IVAS_FRAME_LEN_MS * fs // 1000): - raise ValueError( - "Preamble length has to be integer multiple of frame length for bitstream processing" - ) - # find binary binary = find_binary("gen-patt") @@ -101,9 +88,9 @@ def gen_patt( str(0.001), "-reset", # Reset EID state in between iteractions "-n", - str(int(len_sig / int(IVAS_FRAME_LEN_MS * fs // 1000))), + str(int(len_sig)), "-start", - str(int(start / int(IVAS_FRAME_LEN_MS * fs // 1000)) + 1), + str(int(start) + 1), path_pattern, ] @@ -118,7 +105,6 @@ def create_error_pattern( path_pattern: Union[Path, str], frame_error_rate: float, preamble: Optional[int] = 0, - fs: Optional[int] = 48000, ) -> None: """ Creates error pattern with desired frame error rate for bitstream processing @@ -127,15 +113,13 @@ def create_error_pattern( Parameters ---------- len_sig: int - Length of signal in samples + Length of signal in frames path_pattern: Union[Path, str] Path of output pattern frame_error_rate: float Error rate in percent preamble: Optional[int] - Length of preamble - fs: Optional[int] - Sampling rate + Length of preamble in frames """ with TemporaryDirectory() as tmp_dir: @@ -156,7 +140,6 @@ def create_error_pattern( error_rate=frame_error_rate, path_pattern=path_pattern, start=preamble, - fs=fs, working_dir=tmp_dir, ) diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index b7436a30..fe2a0969 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -42,11 +42,12 @@ ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("dly_error_pro def validate_network_simulator( - error_pattern: Optional[Union[Path, str]] = None, - error_profile: Optional[int] = None, - n_frames_per_paket: Optional[int] = None, + error_pattern: Optional[Union[Path, str]] = None, + error_profile: Optional[int] = None, + n_frames_per_paket: Optional[int] = None, ) -> None: - """Validate settings for the network simulator + """ + Validate settings for the network simulator Parameters ---------- @@ -55,8 +56,9 @@ def validate_network_simulator( error_profile: Optional[int] Index of existing error pattern n_frames_per_paket: Optional[int] - number of frames per paket + Number of frames per paket """ + if find_binary("networkSimulator_g192") is None: raise FileNotFoundError( "The network simulator binary was not found! Please check the configuration." @@ -90,7 +92,7 @@ def network_simulator( n_frames_per_paket: int, ) -> None: """ - Wrapper for eid-xor binary to apply error patterns for the bitstream processing + Wrapper for networkSimulator_g192 binary to apply error patterns for the bitstream processing Parameters ---------- @@ -101,7 +103,7 @@ def network_simulator( out_bitstream: Union[str, Path] Output path for modified bitstream n_frames_per_paket: int, - number of frames per paket [1,2] + Number of frames per paket [1,2] """ # find binary @@ -142,7 +144,7 @@ def apply_network_simulator( n_frames_per_paket: Optional[int] = None, ) -> None: """ - Function to apply a network simulator profile to a bitstreaam + Function to apply a network simulator profile to a bitstreaam Parameters ---------- @@ -155,9 +157,7 @@ def apply_network_simulator( error_profile: Optional[int] Index of existing error pattern n_frames_per_paket: Optional[int] - number of frames per paket - fs: Optional[int] - Sampling rate + Number of frames per paket """ if error_pattern is None: diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index c4615834..0701930f 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -212,32 +212,26 @@ def get_processing_chain( dec_cfg = cond_cfg["dec"] # Frame error pattern bitstream modification - if "tx_fer" in cond_cfg.keys(): - tx_fer_cfg = cond_cfg["tx_fer"] - elif hasattr(cfg, "tx_fer"): - tx_fer_cfg = { - "error_pattern": cfg.tx_fer.get("error_pattern", None), - "error_rate": cfg.tx_fer.get("error_rate", None), - } - else: - tx_fer_cfg = None - - # JBM delay error profile bitstream modification - if "tx_jmb" in cond_cfg.keys(): - tx_jbm_cfg = cond_cfg["tx_jbm"] - elif hasattr(cfg, "tx_jbm"): - tx_jbm_cfg = { - "error_pattern": cfg.tx_jbm.get("error_pattern", None), - "error_profile": cfg.tx_jbm.get("error_profile", None), - "n_frames_per_paket": cfg.tx_jbm.get("n_frames_per_paket", 1), - } + if "tx" in cond_cfg.keys(): + tx_cfg = cond_cfg["tx"] + elif hasattr(cfg, "tx"): + if cfg.tx.get("type", None) == "FER": + tx_cfg = { + "type": cfg.tx.get("type", None), + "error_pattern": cfg.tx.get("error_pattern", None), + "error_rate": cfg.tx.get("error_rate", None), + } + elif cfg.tx.get("type", None) == "JBM": + tx_cfg = { + "type": cfg.tx.get("type", None), + "error_pattern": cfg.tx.get("error_pattern", None), + "error_profile": cfg.tx.get("error_profile", None), + "n_frames_per_paket": cfg.tx.get("n_frames_per_paket", 1), + } + else: + raise ValueError("Type of bitstream procesing either missing or not valid") else: - tx_jbm_cfg = None - - if tx_fer_cfg is not None and tx_jbm_cfg is not None: - raise ValueError( - "Application of error pattern and delay error profile simultaneously is not possible" - ) + tx_cfg = None chain["processes"].append( IVAS( @@ -252,8 +246,7 @@ def get_processing_chain( "dec_bin": dec_cfg.get("bin"), "dec_opts": dec_cfg.get("opts"), "multiprocessing": cfg.multiprocessing, - "tx_jbm": tx_jbm_cfg, - "tx_fer": tx_fer_cfg, + "tx": tx_cfg, } ) ) diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index fcc8ab1e..737c2919 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -204,44 +204,44 @@ class IVAS(Processing): bitstream: Path, logger: Optional[logging.Logger] = None, ) -> Union[Path, str]: - if self.tx_jbm is not None: - bs, ext = os.path.splitext(bitstream) - bitstream_processed = Path(f"{bs}_processed{ext}") - logger.debug(f"Network simulator {bitstream} -> {bitstream_processed}") - apply_network_simulator( - bitstream, - bitstream_processed, - self.tx_jbm["error_pattern"], - self.tx_jbm["error_profile"], - self.tx_jbm["n_frames_per_paket"], - ) - # add -voip cmdline option to the decoder - # TODO: tracefile also? - if self.dec_opts: - if "-voip" not in self.dec_opts: - self.dec_opts.extend(["-voip"]) - - else: - self.dec_opts = ["-voip"] - return bitstream_processed - - elif self.tx_fer is not None: - bs, ext = os.path.splitext(bitstream) - bitstream_processed = Path(f"{bs}_processed{ext}") - signal, _ = read( - in_file, fs=self.in_fs - ) # TODO: pass down number of frames and preamble from concatenation - logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") - create_and_apply_error_pattern( - bitstream, - bitstream_processed, - len(signal), - self.tx_fer["error_pattern"], - self.tx_fer["error_rate"], - fs=self.in_fs, - ) + if self.tx is not None: + if self.tx["type"] == "JBM": + bs, ext = os.path.splitext(bitstream) + bitstream_processed = Path(f"{bs}_processed{ext}") + logger.debug(f"Network simulator {bitstream} -> {bitstream_processed}") + apply_network_simulator( + bitstream, + bitstream_processed, + self.tx["error_pattern"], + self.tx["error_profile"], + self.tx["n_frames_per_paket"], + ) + # add -voip cmdline option to the decoder + # TODO: tracefile also? + if self.dec_opts: + if "-voip" not in self.dec_opts: + self.dec_opts.extend(["-voip"]) + + else: + self.dec_opts = ["-voip"] + return bitstream_processed + + elif self.tx["type"] == "FER": + bs, ext = os.path.splitext(bitstream) + bitstream_processed = Path(f"{bs}_processed{ext}") + signal, _ = read( + in_file, fs=self.in_fs + ) # TODO: pass down number of frames and preamble from concatenation + logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") + create_and_apply_error_pattern( + bitstream, + bitstream_processed, + len(signal), + self.tx["error_pattern"], + self.tx["error_rate"], + ) - return bitstream_processed + return bitstream_processed else: return bitstream -- GitLab From c33730a0e7b1c78d52d32f815a91d9a7b2785c13 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Tue, 18 Apr 2023 14:03:01 +0200 Subject: [PATCH 18/31] small change to fix pipeline --- ivas_processing_scripts/processing/ivas.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 737c2919..805c4c36 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -56,7 +56,7 @@ class IVAS(Processing): self.name = "ivas" self.in_fmt = audio.fromtype(self.in_fmt) self.out_fmt = audio.fromtype(self.out_fmt) - if not hasattr(self,"dec_opts"): + if not hasattr(self, "dec_opts"): self.dec_opts = None def _validate(self): @@ -80,10 +80,14 @@ class IVAS(Processing): ) # TODO: if tx_fer or tx_jbm test for existence of bitstream processing binaries and # existence of error pattern files (if given) already here - if self.tx_jbm is not None: - validate_network_simulator(self.tx_jbm["error_pattern"], - self.tx_jbm["error_profile"], - self.tx_jbm["n_frames_per_paket"]) + if self.tx is not None: + if self.tx.get("type", None) == "JBM": + validate_network_simulator(self.tx["error_pattern"], + self.tx["error_profile"], + self.tx["n_frames_per_paket"]) + elif self.tx.get("type", None) == "FER": + # TODO + pass def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger -- GitLab From 7f0b84fa1bcdefbfe53c09aebf19f5bae439b6e1 Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 14:51:27 +0200 Subject: [PATCH 19/31] updated Readme and template with correct info about JBM processing, fixed a general typo --- README.md | 26 ++++++++++++++----- examples/TEMPLATE.yml | 16 +++++++----- .../audiotools/wrappers/networkSimulator.py | 18 ++++++------- ivas_processing_scripts/processing/chains.py | 2 +- ivas_processing_scripts/processing/ivas.py | 4 +-- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3449c860..94f9bd6a 100755 --- a/README.md +++ b/README.md @@ -222,16 +222,28 @@ input: Click to expand ```yaml -### Bistream processing (transport simulation) done after encoding and before decoding +### Bitstream processing (transport simulation) done after encoding and before decoding ### e.g. frame error insertion or transport simulation for JBM testing +### can be given globally or in individual conditions of type ivas or evs # tx: - ### REQUIRED: Path to network simulation binary - # bs_proc_bin: ".../ivas_python_testscripts/networkSimulator_g192.exe" - ### Path to error pattern (mandatory if no information for generating the error pattern is given) + ### REQUIRED: Type of bitstream processing; possible types: "jbm" or "fer" + #type: "jbm" + + ### JBM + ### REQUIRED: either error_pattern or error_profile + ### delay error profile file # error_pattern: ".../dly_error_profile.dat" - ### options for the binary, possible placeholders are {error_pattern} for the error pattern, - ### {bitstream} for the bitstream to process and {bitstream_processed} for the processed bitstream - # bs_proc_opts: [ "{error_pattern}", "{bitstream}", "{processed_bitstream}", "{processed_bitstream}_tracefile_sim", "2", "0" ] + ### Index of one of the existing delay error profile files to use (1-11) + # error_profile: 5 + ## nFramesPerPacket parameter for the network simulator (optional); default = 1 + # n_frames_per_packet: 2 + + ### FER + ### REQUIRED: either error_pattern or error_rate + ### Frame error pattern file + # error_pattern: "path/pattern.192" + ### Error rate in percent + # error_rate: 5 ``` diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 83b56ae2..28715d7e 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -92,19 +92,21 @@ input: ################################################# ### Bitstream processing ################################################# -### Bistream processing (transport simulation) done after encoding and before decoding +### Bitstream processing (transport simulation) done after encoding and before decoding ### e.g. frame error insertion or transport simulation for JBM testing +### can be given globally here or in individual conditions of type ivas or evs # tx: - ### REQUIRED: Type of bitsream processing; possible types: "jbm" or "fer" + ### REQUIRED: Type of bitstream processing; possible types: "jbm" or "fer" #type: "jbm" ### JBM - ### Path to delay error pattern (mandatory if no information for generating the error pattern is given) + ### REQUIRED: either error_pattern or error_profile + ### delay error profile file # error_pattern: ".../dly_error_profile.dat" - ## - # error_profile: - ## ; defalt = 1 - # n_frames_per_paket: 2 + ### Index of one of the existing delay error profile files to use (1-11) + # error_profile: 5 + ## nFramesPerPacket parameter for the network simulator (optional); default = 1 + # n_frames_per_packet: 2 ### FER ### REQUIRED: either error_pattern or error_rate diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index fe2a0969..27362f55 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -44,7 +44,7 @@ ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("dly_error_pro def validate_network_simulator( error_pattern: Optional[Union[Path, str]] = None, error_profile: Optional[int] = None, - n_frames_per_paket: Optional[int] = None, + n_frames_per_packet: Optional[int] = None, ) -> None: """ Validate settings for the network simulator @@ -77,7 +77,7 @@ def validate_network_simulator( raise ValueError( f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" ) - if n_frames_per_paket is not None and n_frames_per_paket not in [1, 2]: + if n_frames_per_packet is not None and n_frames_per_packet not in [1, 2]: raise ValueError( f"n_frames_per_paket is {n_frames_per_paket}. Should be 1 or 2. Please check your configuration." ) @@ -89,7 +89,7 @@ def network_simulator( error_pattern: Union[str, Path], in_bitstream: Union[str, Path], out_bitstream: Union[str, Path], - n_frames_per_paket: int, + n_frames_per_packet: int, ) -> None: """ Wrapper for networkSimulator_g192 binary to apply error patterns for the bitstream processing @@ -126,7 +126,7 @@ def network_simulator( in_bitstream, out_bitstream, f"{out_bitstream}_tracefile_sim", - str(n_frames_per_paket), + str(n_frames_per_packet), "0" ] @@ -141,7 +141,7 @@ def apply_network_simulator( out_bitstream: Union[Path, str], error_pattern: Optional[Union[Path, str]] = None, error_profile: Optional[int] = None, - n_frames_per_paket: Optional[int] = None, + n_frames_per_packet: Optional[int] = None, ) -> None: """ Function to apply a network simulator profile to a bitstreaam @@ -178,12 +178,12 @@ def apply_network_simulator( "JBM pattern and JBM profile number are specified for bitstream processing. Can't use both" ) - if n_frames_per_paket is None: - n_frames_per_paket = 1 + if n_frames_per_packet is None: + n_frames_per_packet = 1 if error_profile is not None and error_profile == 5: - n_frames_per_paket = 2 + n_frames_per_packet = 2 # apply error pattern - network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_paket) + network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_packet) return diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index 0701930f..79f5740b 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -226,7 +226,7 @@ def get_processing_chain( "type": cfg.tx.get("type", None), "error_pattern": cfg.tx.get("error_pattern", None), "error_profile": cfg.tx.get("error_profile", None), - "n_frames_per_paket": cfg.tx.get("n_frames_per_paket", 1), + "n_frames_per_packet": cfg.tx.get("n_frames_per_packet", 1), } else: raise ValueError("Type of bitstream procesing either missing or not valid") diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 805c4c36..5ae6deb0 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -84,7 +84,7 @@ class IVAS(Processing): if self.tx.get("type", None) == "JBM": validate_network_simulator(self.tx["error_pattern"], self.tx["error_profile"], - self.tx["n_frames_per_paket"]) + self.tx["n_frames_per_packet"]) elif self.tx.get("type", None) == "FER": # TODO pass @@ -218,7 +218,7 @@ class IVAS(Processing): bitstream_processed, self.tx["error_pattern"], self.tx["error_profile"], - self.tx["n_frames_per_paket"], + self.tx["n_frames_per_packet"], ) # add -voip cmdline option to the decoder # TODO: tracefile also? -- GitLab From b89b403bf682540ff2ab9d0ed82015ee3f7d2ed4 Mon Sep 17 00:00:00 2001 From: Stefan Bayer Date: Tue, 18 Apr 2023 14:56:19 +0200 Subject: [PATCH 20/31] added offset to the parameters, small fix --- .../audiotools/wrappers/networkSimulator.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index 27362f55..e9fd6a2a 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -55,7 +55,7 @@ def validate_network_simulator( Path to existing error pattern error_profile: Optional[int] Index of existing error pattern - n_frames_per_paket: Optional[int] + n_frames_per_packet: Optional[int] Number of frames per paket """ @@ -79,7 +79,7 @@ def validate_network_simulator( ) if n_frames_per_packet is not None and n_frames_per_packet not in [1, 2]: raise ValueError( - f"n_frames_per_paket is {n_frames_per_paket}. Should be 1 or 2. Please check your configuration." + f"n_frames_per_paket is {n_frames_per_packet}. Should be 1 or 2. Please check your configuration." ) return @@ -90,6 +90,7 @@ def network_simulator( in_bitstream: Union[str, Path], out_bitstream: Union[str, Path], n_frames_per_packet: int, + offset: int ) -> None: """ Wrapper for networkSimulator_g192 binary to apply error patterns for the bitstream processing @@ -102,8 +103,10 @@ def network_simulator( Path to input bitstream file out_bitstream: Union[str, Path] Output path for modified bitstream - n_frames_per_paket: int, + n_frames_per_packet: int, Number of frames per paket [1,2] + offset: Optional[int] + delay offset """ # find binary @@ -127,7 +130,7 @@ def network_simulator( out_bitstream, f"{out_bitstream}_tracefile_sim", str(n_frames_per_packet), - "0" + str(offset) ] # run command @@ -142,6 +145,7 @@ def apply_network_simulator( error_pattern: Optional[Union[Path, str]] = None, error_profile: Optional[int] = None, n_frames_per_packet: Optional[int] = None, + offset: Optional[int] = 0, ) -> None: """ Function to apply a network simulator profile to a bitstreaam @@ -156,8 +160,10 @@ def apply_network_simulator( Path to existing error pattern error_profile: Optional[int] Index of existing error pattern - n_frames_per_paket: Optional[int] + n_frames_per_packet: Optional[int] Number of frames per paket + offset: Optional[int] + delay offset """ if error_pattern is None: @@ -184,6 +190,6 @@ def apply_network_simulator( n_frames_per_packet = 2 # apply error pattern - network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_packet) + network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_packet, offset) return -- GitLab From e64076196a3f9225569c457607193854c77b2b43 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Tue, 18 Apr 2023 17:55:41 +0200 Subject: [PATCH 21/31] changed sta file handling --- README.md | 4 +-- examples/TEMPLATE.yml | 4 +-- .../audiotools/wrappers/gen_patt.py | 31 ++++++++++++------- ivas_processing_scripts/error_patterns/sta_10 | 9 ------ ivas_processing_scripts/error_patterns/sta_3 | 9 ------ ivas_processing_scripts/error_patterns/sta_5 | 9 ------ ivas_processing_scripts/error_patterns/sta_6 | 9 ------ .../error_patterns/sta_template | 9 ++++++ 8 files changed, 33 insertions(+), 51 deletions(-) delete mode 100644 ivas_processing_scripts/error_patterns/sta_10 delete mode 100644 ivas_processing_scripts/error_patterns/sta_3 delete mode 100644 ivas_processing_scripts/error_patterns/sta_5 delete mode 100644 ivas_processing_scripts/error_patterns/sta_6 create mode 100644 ivas_processing_scripts/error_patterns/sta_template diff --git a/README.md b/README.md index 94f9bd6a..cf7e56a7 100755 --- a/README.md +++ b/README.md @@ -226,8 +226,8 @@ input: ### e.g. frame error insertion or transport simulation for JBM testing ### can be given globally or in individual conditions of type ivas or evs # tx: - ### REQUIRED: Type of bitstream processing; possible types: "jbm" or "fer" - #type: "jbm" + ### REQUIRED: Type of bitstream processing; possible types: "JBM" or "FER" + #type: "JBM" ### JBM ### REQUIRED: either error_pattern or error_profile diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 28715d7e..28a3b3d8 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -96,8 +96,8 @@ input: ### e.g. frame error insertion or transport simulation for JBM testing ### can be given globally here or in individual conditions of type ivas or evs # tx: - ### REQUIRED: Type of bitstream processing; possible types: "jbm" or "fer" - #type: "jbm" + ### REQUIRED: Type of bitstream processing; possible types: "JBM" or "FER" + #type: "JBM" ### JBM ### REQUIRED: either error_pattern or error_profile diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index 72b565f3..7d8778c3 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -108,7 +108,6 @@ def create_error_pattern( ) -> None: """ Creates error pattern with desired frame error rate for bitstream processing - For 3, 5, 6 and 10 percent FER the results are deterministic Parameters ---------- @@ -124,16 +123,26 @@ def create_error_pattern( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - if frame_error_rate in LIST_FER: - # copy sta file - sta_file = ERROR_PATTERNS_DIR.joinpath(f"sta_{int(frame_error_rate)}") - tmp_sta_file = tmp_dir.joinpath("sta") - shutil.copyfile(sta_file, tmp_sta_file) - - else: - warnings.warn( - f"Frame error rate {frame_error_rate}% is not a default rate. Error pattern is random and not deterministic." - ) + + sta_file = ERROR_PATTERNS_DIR.joinpath(f"sta_template") + tmp_sta_file = tmp_dir.joinpath("sta") + # TODO: compute seed + seed = 0 + # open file and modify + lines = [] + with open(sta_file, "r") as sta_file_txt: + lines.append(sta_file_txt.readline()) # not changed + lines.append(f"{sta_file_txt.readline()[:-2]}{frame_error_rate/100}\n") + lines.append(sta_file_txt.readline()) # not changed + lines.append(f"{sta_file_txt.readline()[:-2]}{seed}\n") + lines.append(sta_file_txt.readline()) # not changed + lines.append(f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n") + lines.append(sta_file_txt.readline()) # not changed + lines.append(f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n") + lines.append(sta_file_txt.readline()) # not changed + + with open(tmp_sta_file, "w") as tmp_sta_file_txt: + tmp_sta_file_txt.write("".join(lines)) gen_patt( len_sig=len_sig, diff --git a/ivas_processing_scripts/error_patterns/sta_10 b/ivas_processing_scripts/error_patterns/sta_10 deleted file mode 100644 index cf8517ae..00000000 --- a/ivas_processing_scripts/error_patterns/sta_10 +++ /dev/null @@ -1,9 +0,0 @@ -EID -BER = 0.100000 -GAMMA = 0.000000 -RAN-seed = 0x356007f8 -Current State = B -GOOD->GOOD = 0.800000 -GOOD->BAD = 1.000000 -BAD ->GOOD = 0.800000 -BAD ->BAD = 1.000000 diff --git a/ivas_processing_scripts/error_patterns/sta_3 b/ivas_processing_scripts/error_patterns/sta_3 deleted file mode 100644 index c2f2c35e..00000000 --- a/ivas_processing_scripts/error_patterns/sta_3 +++ /dev/null @@ -1,9 +0,0 @@ -EID -BER = 0.030000 -GAMMA = 0.000000 -RAN-seed = 0xab5d4825 -Current State = G -GOOD->GOOD = 0.940000 -GOOD->BAD = 1.000000 -BAD ->GOOD = 0.940000 -BAD ->BAD = 1.000000 \ No newline at end of file diff --git a/ivas_processing_scripts/error_patterns/sta_5 b/ivas_processing_scripts/error_patterns/sta_5 deleted file mode 100644 index c872f3a6..00000000 --- a/ivas_processing_scripts/error_patterns/sta_5 +++ /dev/null @@ -1,9 +0,0 @@ -EID -BER = 0.050000 -GAMMA = 0.000000 -RAN-seed = 0x6d30b654 -Current State = G -GOOD->GOOD = 0.900000 -GOOD->BAD = 1.000000 -BAD ->GOOD = 0.900000 -BAD ->BAD = 1.000000 diff --git a/ivas_processing_scripts/error_patterns/sta_6 b/ivas_processing_scripts/error_patterns/sta_6 deleted file mode 100644 index 55e17ec2..00000000 --- a/ivas_processing_scripts/error_patterns/sta_6 +++ /dev/null @@ -1,9 +0,0 @@ -EID -BER = 0.060000 -GAMMA = 0.000000 -RAN-seed = 0x176d71ac -Current State = G -GOOD->GOOD = 0.880000 -GOOD->BAD = 1.000000 -BAD ->GOOD = 0.880000 -BAD ->BAD = 1.000000 \ No newline at end of file diff --git a/ivas_processing_scripts/error_patterns/sta_template b/ivas_processing_scripts/error_patterns/sta_template new file mode 100644 index 00000000..0683ed82 --- /dev/null +++ b/ivas_processing_scripts/error_patterns/sta_template @@ -0,0 +1,9 @@ +EID +BER = x +GAMMA = 0.000000 +RAN-seed = x +Current State = G +GOOD->GOOD = x +GOOD->BAD = 1.000000 +BAD ->GOOD = x +BAD ->BAD = 1.000000 \ No newline at end of file -- GitLab From 70a510856d3e46da6f9b022b6b6379af9acd3fd3 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Tue, 18 Apr 2023 18:56:58 +0200 Subject: [PATCH 22/31] added seed and random function for bitstream processing --- .../audiotools/wrappers/eid_xor.py | 8 +- .../audiotools/wrappers/gen_patt.py | 15 +++- .../audiotools/wrappers/random_seed.py | 83 +++++++++++++++++++ ivas_processing_scripts/processing/ivas.py | 1 + 4 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 ivas_processing_scripts/audiotools/wrappers/random_seed.py diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py index c9141ff2..dc7f87f0 100644 --- a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -91,6 +91,8 @@ def create_and_apply_error_pattern( error_pattern: Optional[Union[Path, str]] = None, error_rate: Optional[float] = None, preamble: Optional[int] = 0, + master_seed: Optional[int] = 0, + prerun_seed: Optional[int] = 0, ) -> None: """ Function to create (or use existing) frame error pattern for bitstream processing @@ -109,6 +111,10 @@ def create_and_apply_error_pattern( Error rate in percent preamble: Optional[int] Length of preamble in frames + master_seed: Optional[int] + Master seed for error pattern generation + prerun_seed: Optional[int] + Number of preruns in seed generation """ if error_pattern is None: @@ -117,7 +123,7 @@ def create_and_apply_error_pattern( error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix( ".192" ) - create_error_pattern(len_sig, error_pattern, error_rate, preamble) + create_error_pattern(len_sig, error_pattern, error_rate, preamble, master_seed, prerun_seed) else: raise ValueError( "Either error pattern or error rate has to be specified for bitstream processing" diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index 7d8778c3..b1e7c424 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -37,10 +37,9 @@ from pathlib import Path from tempfile import TemporaryDirectory from typing import Optional, Union -from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS from ivas_processing_scripts.utils import find_binary, run +from ivas_processing_scripts.audiotools.wrappers.random_seed import random_seed -LIST_FER = [3, 5, 6, 10] ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("error_patterns") @@ -105,6 +104,8 @@ def create_error_pattern( path_pattern: Union[Path, str], frame_error_rate: float, preamble: Optional[int] = 0, + master_seed: Optional[int] = 0, + prerun_seed: Optional[int] = 0, ) -> None: """ Creates error pattern with desired frame error rate for bitstream processing @@ -119,6 +120,10 @@ def create_error_pattern( Error rate in percent preamble: Optional[int] Length of preamble in frames + master_seed: Optional[int] + Master seed for error pattern generation + prerun_seed: optional[int] + Number of preruns in seed generation """ with TemporaryDirectory() as tmp_dir: @@ -126,8 +131,10 @@ def create_error_pattern( sta_file = ERROR_PATTERNS_DIR.joinpath(f"sta_template") tmp_sta_file = tmp_dir.joinpath("sta") - # TODO: compute seed - seed = 0 + + # compute seed + seed = random_seed(master_seed, prerun_seed) + # open file and modify lines = [] with open(sta_file, "r") as sta_file_txt: diff --git a/ivas_processing_scripts/audiotools/wrappers/random_seed.py b/ivas_processing_scripts/audiotools/wrappers/random_seed.py new file mode 100644 index 00000000..c7b8e057 --- /dev/null +++ b/ivas_processing_scripts/audiotools/wrappers/random_seed.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +# +# (C) 2022-2023 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 typing import Optional +from ivas_processing_scripts.utils import find_binary, run + + +def random_seed( + master_seed: Optional[int] = 0, + prerun_seed: Optional[int] = 0, + hexa: Optional[bool] = True, +) -> int: + """ + + Parameters + ---------- + master_seed: Optional[int] + Master seed for error pattern generation + prerun_seed: Optional[int] + Number of preruns in seed generation + hexa: Optonal[bool] + Flag if output should be in hexadecimal or decimal format + + Returns + ------- + result: int + One random value + """ + + # find binary + binary = find_binary("random") + + # set up command line + cmd = [ + str(binary), + "-n", # Number of items + str(1), + "-s", + str(master_seed), + "-d", + str(prerun_seed), + "-r", # value range for results + str(0), + str(99999999) + ] + + # run command + result = run(cmd) + result = int(result.stdout[:-1]) + + if hexa: + result = hex(result) + + return result diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 5ae6deb0..e923cdf8 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -236,6 +236,7 @@ class IVAS(Processing): signal, _ = read( in_file, fs=self.in_fs ) # TODO: pass down number of frames and preamble from concatenation + # TODO: pass seed logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") create_and_apply_error_pattern( bitstream, -- GitLab From e304f83cc552d29e1f0b4e0cfc72c0683e0d328d Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 12:14:40 +0200 Subject: [PATCH 23/31] added seeds to config --- examples/TEMPLATE.yml | 6 +++++- ivas_processing_scripts/constants.py | 2 ++ ivas_processing_scripts/processing/chains.py | 2 ++ ivas_processing_scripts/processing/ivas.py | 13 +++++++------ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 28a3b3d8..4ec760d9 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -14,6 +14,10 @@ ### Deletion of temporary directories containing ### intermediate processing files, bitstreams etc.; default = false # delete_tmp: true +### Master seed for random processes like bitstream error pattern generation; default = 0 +# master_seed: 5 +### Additional seed to specify number of preruns; default = 0 +# prerun_seed: 2 ### Any relative paths will be interpreted relative to the working directory the script is called from! ### Usage of absolute paths is recommended. @@ -105,7 +109,7 @@ input: # error_pattern: ".../dly_error_profile.dat" ### Index of one of the existing delay error profile files to use (1-11) # error_profile: 5 - ## nFramesPerPacket parameter for the network simulator (optional); default = 1 + ## nFramesPerPacket parameter for the network simulator; default = 1 # n_frames_per_packet: 2 ### FER diff --git a/ivas_processing_scripts/constants.py b/ivas_processing_scripts/constants.py index 16e7d264..9b3ef303 100755 --- a/ivas_processing_scripts/constants.py +++ b/ivas_processing_scripts/constants.py @@ -58,6 +58,8 @@ DEFAULT_CONFIG = { "git_sha": f"{get_gitsha()}", "multiprocessing": True, "delete_tmp": False, + "master_seed": 0, + "prerun_seed": 0, "concatenate_input": False, "concat_silence": { "pre": 0, diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index 79f5740b..6afab850 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -220,6 +220,8 @@ def get_processing_chain( "type": cfg.tx.get("type", None), "error_pattern": cfg.tx.get("error_pattern", None), "error_rate": cfg.tx.get("error_rate", None), + "master_seed": cfg.master_seed, + "prerun_seed": cfg.prerun_seed, } elif cfg.tx.get("type", None) == "JBM": tx_cfg = { diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index e923cdf8..97f813e6 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -236,14 +236,15 @@ class IVAS(Processing): signal, _ = read( in_file, fs=self.in_fs ) # TODO: pass down number of frames and preamble from concatenation - # TODO: pass seed logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") create_and_apply_error_pattern( - bitstream, - bitstream_processed, - len(signal), - self.tx["error_pattern"], - self.tx["error_rate"], + in_bitstream=bitstream, + out_bitstream=bitstream_processed, + len_sig=len(signal), + error_pattern=self.tx["error_pattern"], + error_rate=self.tx["error_rate"], + master_seed=self.tx["master_seed"], + prerun_seed=self.tx["prerun_seed"], ) return bitstream_processed -- GitLab From 09920fb76f2dbab32d9a863ba51cde813cc42704 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 15:08:26 +0200 Subject: [PATCH 24/31] updated readme --- README.md | 26 ++++++++++++------- ivas_processing_scripts/__init__.py | 6 +++++ .../audiotools/audiofile.py | 20 +++++++++++--- .../processing/processing.py | 3 ++- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cf7e56a7..13cda76b 100755 --- a/README.md +++ b/README.md @@ -432,7 +432,9 @@ No required arguments but the `type` key. #### EVS For EVS a list of at least one bitrate has to be specified with the key `bitrates`. The entries in this list can also be lists containing the bitrates used for the processing of the individual channels. This configuration has to match the channel configuration. If the provided list is shorter, the last value will be repeated. -For the encoding stage `cod` and the decoding stage `dec`, the path to the IVAS_cod and IVAS_dec binaries can be specified under the key `bin`. Additionally some resampling can be applied by using the key `fs` followed by the desired sampling rate. +For the encoding stage `cod` and the decoding stage `dec`, the path to the IVAS_cod and IVAS_dec binaries can be specified under the key `bin`. +Additionally some resampling can be applied by using the key `fs` followed by the desired sampling rate. +The general bitstream processing configuration can be locally overwritten for each EVS and IVAS condition with the key `tx`. #### IVAS The configuration of the IVAS condition is similar to the EVS condition. However, only one bitrate for all channels (and metadata) can be specified. In addition to that, the encoder and decoder take some additional arguments defined by the key `opts`. @@ -476,15 +478,19 @@ It also makes use of the MASA tools provided by Nokia. These can be found here: The following binaries/executables are needed for the different processing steps: -| processing step | ITU binary | -|--------------------------|-----------------| -| LP filtering | filter | -| HP filtering | filter | -| Resampling | filter | -| Loudness adjustment | bs1770demo | -| MNRU | p50fbmnru | -| ESDRU | esdru | -| MASA rendering | masaRenderer | +| processing step | Exectuable | Where to find | +|-------------------------------------|---------------------------|---------------| +| LP filtering | filter | | +| HP filtering | filter | | +| Resampling | filter | | +| Loudness adjustment | bs1770demo | | +| MNRU | p50fbmnru | | +| ESDRU | esdru | | +| MASA rendering | masaRenderer | | +| Frame error pattern generation | gen-patt | | +| Frame error pattern application | eid-xor | | +| Random seed generation for gen-patt | random | | +| JBM error pattern generation | networkSimulator_g192 | | The necessary binaries have to be placed in the [ivas_processing_scripts/bin](./ivas_processing_scripts/bin) folder. For the ITU tools it is sufficient to copy the binaries while it is necessary to add some additional files for the MASA renderer. diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index e51fdf34..4040dd80 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -130,6 +130,12 @@ def main(args): logger.info(f" Generating condition: {condition['name']}") + # # TODO: what happens when no concatenation or only one file for concatenation? + # if condition["processes"][0].name == "ivas": # TODO: check if 0 index sufficient + # a = {"number_frames": cfg.num_frames, "number_frames_preamble": cfg.num_frames_preamble} + # condition["processes"][0].tx.update(a) + + apply_func_parallel( process_item, zip( diff --git a/ivas_processing_scripts/audiotools/audiofile.py b/ivas_processing_scripts/audiotools/audiofile.py index d08559d7..6706fdab 100755 --- a/ivas_processing_scripts/audiotools/audiofile.py +++ b/ivas_processing_scripts/audiotools/audiofile.py @@ -39,6 +39,7 @@ import numpy as np import scipy.io.wavfile as wav from .audioarray import trim, window +from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS logger = logging.getLogger("__main__") logger.setLevel(logging.DEBUG) @@ -156,7 +157,7 @@ def concat( pad_noise: Optional[bool] = False, preamble: Optional[int] = None, pad_noise_preamble: Optional[bool] = False, -) -> list: +) -> Tuple[list, int, int]: """ Horizontally concatenates audio files into one long file @@ -177,7 +178,12 @@ def concat( Returns ------- - List of sample indices to split the resulting file at + splits + List of sample indices to split the resulting file at + num_frames + Length of overall signal in frames + num_frames_preamble: int + Length of preamble in frames """ y = None @@ -211,7 +217,15 @@ def concat( write(out_file, y, fs=in_fs) - return splits[1:] + # compute length of concatenated file and preamble in frames for bitstream processing + frame_len = fs_compare * IVAS_FRAME_LEN_MS / 1000 + num_frames = int(len(y) / frame_len) + if preamble: + num_frames_preamble = int(preamble / IVAS_FRAME_LEN_MS) + else: + num_frames_preamble = 0 + + return splits[1:], num_frames, num_frames_preamble def split( diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 847a8d11..0b7cf3bd 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -94,7 +94,7 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): f"{cfg.input_path.name}_concatenated.wav" ) - cfg.splits = concat( + cfg.splits, cfg.num_frames, cfg.num_frames_preamble = concat( cfg.items_list, cfg.concat_file, cfg.concat_silence.get("pre", 0), @@ -126,6 +126,7 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): "Concatenation specified with a single item this will have no effect. Please use preprocessing if padding is required." ) cfg.splits = [] + # TODO: set num frames def concat_teardown(cfg: TestConfig, logger: logging.Logger): -- GitLab From 04bd036994ef2ef92d93492dc83ddb65e925e190 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Wed, 19 Apr 2023 16:23:47 +0200 Subject: [PATCH 25/31] update info on executables --- ivas_processing_scripts/bin/README.txt | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ivas_processing_scripts/bin/README.txt b/ivas_processing_scripts/bin/README.txt index 0aa3037d..624af21e 100755 --- a/ivas_processing_scripts/bin/README.txt +++ b/ivas_processing_scripts/bin/README.txt @@ -1,9 +1,16 @@ -Necessary binaries/executables: -- ITU tools: https://github.com/openitu/STL (deprecated filter function) or https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip - - bs1770demo used for loudness measurement and adjustment - - filter used for 50Hz high-pass filtering, 3.5kHz and 7kHz low-pass filtering and resampling - - esdru used for ESDRU condition - - p50fbmnru used for MNRU condition -- MASA tools: https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip - - masaRenderer used for rendering of MASA signals to binaural, 7.1+4 and 5.1 format (Attention: some files have to be in the same directory as the renderer) +Necessary additional executables: + +| processing step | Executable | Where to find | +|---------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------| +| LP filtering | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| HP filtering | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| Resampling | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| Loudness adjustment | bs1770demo | https://github.com/openitu/STL | +| MNRU | p50fbmnru | https://github.com/openitu/STL | +| ESDRU | esdru | https://github.com/openitu/STL | +| MASA rendering | masaRenderer | https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip | +| Frame error pattern generation | gen-patt | https://www.itu.int/rec/T-REC-G.191-201003-S/en (Note: Version in https://github.com/openitu/STL is buggy!) | +| Frame error pattern application | eid-xor | https://github.com/openitu/STL | +| Random offset/seed generation | random | https://github.com/openitu/STL | +| JBM network similulator | networkSimulator_g192 | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | -- GitLab From b219f3d31a49d7eb4a10ecba4b38119895995653 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Wed, 19 Apr 2023 16:27:48 +0200 Subject: [PATCH 26/31] sort lines according to download place --- ivas_processing_scripts/bin/README.txt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ivas_processing_scripts/bin/README.txt b/ivas_processing_scripts/bin/README.txt index 624af21e..2bbdd44f 100755 --- a/ivas_processing_scripts/bin/README.txt +++ b/ivas_processing_scripts/bin/README.txt @@ -1,16 +1,14 @@ Necessary additional executables: -| processing step | Executable | Where to find | +| Processing step | Executable | Where to find | |---------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------| -| LP filtering | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | -| HP filtering | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | -| Resampling | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | | Loudness adjustment | bs1770demo | https://github.com/openitu/STL | | MNRU | p50fbmnru | https://github.com/openitu/STL | | ESDRU | esdru | https://github.com/openitu/STL | -| MASA rendering | masaRenderer | https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip | -| Frame error pattern generation | gen-patt | https://www.itu.int/rec/T-REC-G.191-201003-S/en (Note: Version in https://github.com/openitu/STL is buggy!) | | Frame error pattern application | eid-xor | https://github.com/openitu/STL | -| Random offset/seed generation | random | https://github.com/openitu/STL | +| Error pattern generation | gen-patt | https://www.itu.int/rec/T-REC-G.191-201003-S/en (Note: Version in https://github.com/openitu/STL is buggy!) | +| Filtering, Resampling | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| Random offset/seed generation | random | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | | JBM network similulator | networkSimulator_g192 | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| MASA rendering | masaRenderer | https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip | -- GitLab From f62ad734aacbad685d0299f2490158d5b06fae78 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 16:29:00 +0200 Subject: [PATCH 27/31] fixed error pattern application --- .../audiotools/audiofile.py | 16 +- .../audiotools/wrappers/eid_xor.py | 4 +- ivas_processing_scripts/processing/chains.py | 1 + ivas_processing_scripts/processing/ivas.py | 14 +- .../processing/processing.py | 141 ++++++++---------- 5 files changed, 77 insertions(+), 99 deletions(-) diff --git a/ivas_processing_scripts/audiotools/audiofile.py b/ivas_processing_scripts/audiotools/audiofile.py index 6706fdab..d7f3c2f4 100755 --- a/ivas_processing_scripts/audiotools/audiofile.py +++ b/ivas_processing_scripts/audiotools/audiofile.py @@ -157,7 +157,7 @@ def concat( pad_noise: Optional[bool] = False, preamble: Optional[int] = None, pad_noise_preamble: Optional[bool] = False, -) -> Tuple[list, int, int]: +) -> list: """ Horizontally concatenates audio files into one long file @@ -180,10 +180,6 @@ def concat( ------- splits List of sample indices to split the resulting file at - num_frames - Length of overall signal in frames - num_frames_preamble: int - Length of preamble in frames """ y = None @@ -217,15 +213,7 @@ def concat( write(out_file, y, fs=in_fs) - # compute length of concatenated file and preamble in frames for bitstream processing - frame_len = fs_compare * IVAS_FRAME_LEN_MS / 1000 - num_frames = int(len(y) / frame_len) - if preamble: - num_frames_preamble = int(preamble / IVAS_FRAME_LEN_MS) - else: - num_frames_preamble = 0 - - return splits[1:], num_frames, num_frames_preamble + return splits[1:] def split( diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py index dc7f87f0..284f4581 100644 --- a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -120,9 +120,7 @@ def create_and_apply_error_pattern( if error_pattern is None: # create error pattern if error_rate is not None: - error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix( - ".192" - ) + error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix(".192") create_error_pattern(len_sig, error_pattern, error_rate, preamble, master_seed, prerun_seed) else: raise ValueError( diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index 6afab850..98193ea6 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -249,6 +249,7 @@ def get_processing_chain( "dec_opts": dec_cfg.get("opts"), "multiprocessing": cfg.multiprocessing, "tx": tx_cfg, + "preamble": cfg.preamble, } ) ) diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 97f813e6..3581d53c 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -33,7 +33,6 @@ import logging import os.path import platform -from copy import deepcopy from pathlib import Path from typing import Optional, Union @@ -47,6 +46,7 @@ from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( ) from ivas_processing_scripts.processing.processing import Processing from ivas_processing_scripts.utils import run +from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS class IVAS(Processing): @@ -233,14 +233,18 @@ class IVAS(Processing): elif self.tx["type"] == "FER": bs, ext = os.path.splitext(bitstream) bitstream_processed = Path(f"{bs}_processed{ext}") - signal, _ = read( - in_file, fs=self.in_fs - ) # TODO: pass down number of frames and preamble from concatenation + signal, _ = read(in_file, fs=self.in_fs, nchannels=self.in_fmt.num_channels) + frame_number = len(signal) / self.in_fs / IVAS_FRAME_LEN_MS * 1000 + if self.preamble: + frame_number_preamble = self.preamble / IVAS_FRAME_LEN_MS + else: + frame_number_preamble = 0 logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") create_and_apply_error_pattern( in_bitstream=bitstream, out_bitstream=bitstream_processed, - len_sig=len(signal), + len_sig=frame_number, + preamble=frame_number_preamble, error_pattern=self.tx["error_pattern"], error_rate=self.tx["error_rate"], master_seed=self.tx["master_seed"], diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 0b7cf3bd..b50137ed 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -63,70 +63,62 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): if any([i for i in cfg.items_list if i.suffix == ".txt"]): raise SystemExit("Concatenation for text files is unsupported") - if len(cfg.items_list) > 1: - logger.info(f"Concatenating input files in directory {cfg.input_path}") - - # concatenate ISM metadata - if cfg.input["fmt"].startswith("ISM"): - cfg.concat_meta = [] - for obj_idx in range(len(cfg.metadata_path[0])): - cfg.concat_meta.append( - cfg.output_path.joinpath( - f"{cfg.input_path.name}_concatenated.wav.{obj_idx}.csv" - ) + logger.info(f"Concatenating input files in directory {cfg.input_path}") + + # concatenate ISM metadata + if cfg.input["fmt"].startswith("ISM"): + cfg.concat_meta = [] + for obj_idx in range(len(cfg.metadata_path[0])): + cfg.concat_meta.append( + cfg.output_path.joinpath( + f"{cfg.input_path.name}_concatenated.wav.{obj_idx}.csv" ) - - concat_meta_from_file( - cfg.items_list, - cfg.metadata_path, - cfg.concat_meta, - cfg.concat_silence.get("pre", 0), - cfg.concat_silence.get("post", 0), - cfg.input["fmt"], - preamble=cfg.preamble, ) - # set input to the concatenated file we have just written to the output dir - cfg.metadata_path = [cfg.concat_meta] - - # concatenate audio - cfg.concat_file = cfg.output_path.joinpath( - f"{cfg.input_path.name}_concatenated.wav" - ) - - cfg.splits, cfg.num_frames, cfg.num_frames_preamble = concat( + concat_meta_from_file( cfg.items_list, - cfg.concat_file, + cfg.metadata_path, + cfg.concat_meta, cfg.concat_silence.get("pre", 0), cfg.concat_silence.get("post", 0), - cfg.input.get("fs", 48000), + cfg.input["fmt"], preamble=cfg.preamble, - pad_noise_preamble=cfg.pad_noise_preamble, ) - # save item naming for splits naming in the end - cfg.split_names = [] - for name in cfg.items_list: - cfg.split_names.append(Path(name).stem.split(".")[0]) # set input to the concatenated file we have just written to the output dir - cfg.items_list = [cfg.concat_file] + cfg.metadata_path = [cfg.concat_meta] - # write out splits - with open(cfg.concat_file.with_suffix(".splits.log"), "w") as f: - print(", ".join([str(s) for s in cfg.splits]), file=f) - print(", ".join([str(sn) for sn in cfg.split_names]), file=f) - print(", ".join([str(i.stem) for i in cfg.items_list]), file=f) + # concatenate audio + cfg.concat_file = cfg.output_path.joinpath( + f"{cfg.input_path.name}_concatenated.wav" + ) - logger.info( - f"Splits written to file {cfg.concat_file.with_suffix('.splits.log')}" - ) + cfg.splits = concat( + cfg.items_list, + cfg.concat_file, + cfg.concat_silence.get("pre", 0), + cfg.concat_silence.get("post", 0), + cfg.input.get("fs", 48000), + preamble=cfg.preamble, + pad_noise_preamble=cfg.pad_noise_preamble, + ) - else: - warn( - "Concatenation specified with a single item this will have no effect. Please use preprocessing if padding is required." - ) - cfg.splits = [] - # TODO: set num frames + # save item naming for splits naming in the end + cfg.split_names = [] + for name in cfg.items_list: + cfg.split_names.append(Path(name).stem.split(".")[0]) + # set input to the concatenated file we have just written to the output dir + cfg.items_list = [cfg.concat_file] + + # write out splits + with open(cfg.concat_file.with_suffix(".splits.log"), "w") as f: + print(", ".join([str(s) for s in cfg.splits]), file=f) + print(", ".join([str(sn) for sn in cfg.split_names]), file=f) + print(", ".join([str(i.stem) for i in cfg.items_list]), file=f) + + logger.info( + f"Splits written to file {cfg.concat_file.with_suffix('.splits.log')}" + ) def concat_teardown(cfg: TestConfig, logger: logging.Logger): @@ -140,37 +132,32 @@ def concat_teardown(cfg: TestConfig, logger: logging.Logger): out_files = [] out_meta = [] - if num_splits <= 1: - logger.info("No splitting of output file necessary since only one signal used.") - out_meta = None + logger.info(f"Splitting output file in directory {cfg.output_path}") - else: - logger.info(f"Splitting output file in directory {cfg.output_path}") + for odir in cfg.out_dirs: + path_input = odir / cfg.items_list[0].name + out_paths = split( + path_input, odir, cfg.split_names, cfg.splits, preamble=cfg.preamble + ) + + logger.debug( + f"Resulting split files condition {odir.name}: {', '.join([str(op) for op in out_paths])}" + ) + out_files.append(out_paths) + # split ISM metadata + if output_format.startswith("ISM"): for odir in cfg.out_dirs: path_input = odir / cfg.items_list[0].name - out_paths = split( - path_input, odir, cfg.split_names, cfg.splits, preamble=cfg.preamble - ) - - logger.debug( - f"Resulting split files condition {odir.name}: {', '.join([str(op) for op in out_paths])}" + out_meta_paths = split_meta_in_file( + path_input, + odir, + cfg.split_names, + cfg.splits, + output_format, + preamble=cfg.preamble, ) - out_files.append(out_paths) - - # split ISM metadata - if output_format.startswith("ISM"): - for odir in cfg.out_dirs: - path_input = odir / cfg.items_list[0].name - out_meta_paths = split_meta_in_file( - path_input, - odir, - cfg.split_names, - cfg.splits, - output_format, - preamble=cfg.preamble, - ) - out_meta.append(out_meta_paths) + out_meta.append(out_meta_paths) # remove concatenated file if cfg.delete_tmp: -- GitLab From 78510d93bd3fefe730fe5037b7fde3a71fb71d6e Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 16:34:34 +0200 Subject: [PATCH 28/31] added executable info in main README --- README.md | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 13cda76b..e5731507 100755 --- a/README.md +++ b/README.md @@ -472,28 +472,22 @@ The processing chain is as follows: --- ## ITU Tools -This module uses the ITU audio processing tools. These tools can be found here: https://github.com/openitu/STL (except for the filter binary which is deprecated).
-The filter binary with all necessary filter types can be found here: https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip.
-It also makes use of the MASA tools provided by Nokia. These can be found here: https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip. - The following binaries/executables are needed for the different processing steps: -| processing step | Exectuable | Where to find | -|-------------------------------------|---------------------------|---------------| -| LP filtering | filter | | -| HP filtering | filter | | -| Resampling | filter | | -| Loudness adjustment | bs1770demo | | -| MNRU | p50fbmnru | | -| ESDRU | esdru | | -| MASA rendering | masaRenderer | | -| Frame error pattern generation | gen-patt | | -| Frame error pattern application | eid-xor | | -| Random seed generation for gen-patt | random | | -| JBM error pattern generation | networkSimulator_g192 | | +| Processing step | Executable | Where to find | +|---------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------| +| Loudness adjustment | bs1770demo | https://github.com/openitu/STL | +| MNRU | p50fbmnru | https://github.com/openitu/STL | +| ESDRU | esdru | https://github.com/openitu/STL | +| Frame error pattern application | eid-xor | https://github.com/openitu/STL | +| Error pattern generation | gen-patt | https://www.itu.int/rec/T-REC-G.191-201003-S/en (Note: Version in https://github.com/openitu/STL is buggy!) | +| Filtering, Resampling | filter | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| Random offset/seed generation | random | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| JBM network similulator | networkSimulator_g192 | https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_76/docs/S4-131277.zip | +| MASA rendering | masaRenderer | https://www.3gpp.org/ftp/TSG_SA/WG4_CODEC/TSGS4_122_Athens/Docs/S4-230221.zip | The necessary binaries have to be placed in the [ivas_processing_scripts/bin](./ivas_processing_scripts/bin) folder. -For the ITU tools it is sufficient to copy the binaries while it is necessary to add some additional files for the MASA renderer. +For most of the tools it is sufficient to copy the binaries while it is necessary to add some additional files for the MASA renderer. --- -- GitLab From 6d51bf098dee98a43e413192bc4a6b0f7f50545a Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 17:00:10 +0200 Subject: [PATCH 29/31] added bitstream modification for EVS --- ivas_processing_scripts/__init__.py | 1 - .../audiotools/audiofile.py | 3 +- .../audiotools/wrappers/eid_xor.py | 8 +- .../audiotools/wrappers/gen_patt.py | 10 ++- .../audiotools/wrappers/networkSimulator.py | 14 ++-- .../audiotools/wrappers/random_seed.py | 3 +- ivas_processing_scripts/processing/chains.py | 32 +++++++- ivas_processing_scripts/processing/evs.py | 74 ++++++++++++++++++- ivas_processing_scripts/processing/ivas.py | 21 ++++-- .../processing/processing.py | 4 +- 10 files changed, 144 insertions(+), 26 deletions(-) diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index 4040dd80..e234efd2 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -135,7 +135,6 @@ def main(args): # a = {"number_frames": cfg.num_frames, "number_frames_preamble": cfg.num_frames_preamble} # condition["processes"][0].tx.update(a) - apply_func_parallel( process_item, zip( diff --git a/ivas_processing_scripts/audiotools/audiofile.py b/ivas_processing_scripts/audiotools/audiofile.py index d7f3c2f4..e33b0d57 100755 --- a/ivas_processing_scripts/audiotools/audiofile.py +++ b/ivas_processing_scripts/audiotools/audiofile.py @@ -38,9 +38,10 @@ from typing import Optional, Tuple, Union import numpy as np import scipy.io.wavfile as wav -from .audioarray import trim, window from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS +from .audioarray import trim, window + logger = logging.getLogger("__main__") logger.setLevel(logging.DEBUG) diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py index 284f4581..6d36ff22 100644 --- a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -120,8 +120,12 @@ def create_and_apply_error_pattern( if error_pattern is None: # create error pattern if error_rate is not None: - error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix(".192") - create_error_pattern(len_sig, error_pattern, error_rate, preamble, master_seed, prerun_seed) + error_pattern = in_bitstream.parent.joinpath("error_pattern").with_suffix( + ".192" + ) + create_error_pattern( + len_sig, error_pattern, error_rate, preamble, master_seed, prerun_seed + ) else: raise ValueError( "Either error pattern or error rate has to be specified for bitstream processing" diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index b1e7c424..71d8d93f 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -37,8 +37,8 @@ from pathlib import Path from tempfile import TemporaryDirectory from typing import Optional, Union -from ivas_processing_scripts.utils import find_binary, run from ivas_processing_scripts.audiotools.wrappers.random_seed import random_seed +from ivas_processing_scripts.utils import find_binary, run ERROR_PATTERNS_DIR = Path(__file__).parent.parent.parent.joinpath("error_patterns") @@ -143,9 +143,13 @@ def create_error_pattern( lines.append(sta_file_txt.readline()) # not changed lines.append(f"{sta_file_txt.readline()[:-2]}{seed}\n") lines.append(sta_file_txt.readline()) # not changed - lines.append(f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n") + lines.append( + f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n" + ) lines.append(sta_file_txt.readline()) # not changed - lines.append(f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n") + lines.append( + f"{sta_file_txt.readline()[:-2]}{1-(frame_error_rate/100*2)}\n" + ) lines.append(sta_file_txt.readline()) # not changed with open(tmp_sta_file, "w") as tmp_sta_file_txt: diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index e9fd6a2a..feb24891 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -30,9 +30,9 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # +import os.path from pathlib import Path from typing import Optional, Union -import os.path from ivas_processing_scripts.audiotools.wrappers.gen_patt import create_error_pattern from ivas_processing_scripts.utils import find_binary, run @@ -90,7 +90,7 @@ def network_simulator( in_bitstream: Union[str, Path], out_bitstream: Union[str, Path], n_frames_per_packet: int, - offset: int + offset: int, ) -> None: """ Wrapper for networkSimulator_g192 binary to apply error patterns for the bitstream processing @@ -130,7 +130,7 @@ def network_simulator( out_bitstream, f"{out_bitstream}_tracefile_sim", str(n_frames_per_packet), - str(offset) + str(offset), ] # run command @@ -170,7 +170,9 @@ def apply_network_simulator( # create error pattern if error_profile is not None: if error_profile in LIST_JBM_PROFILES: - error_pattern = ERROR_PATTERNS_DIR.joinpath(f"dly_error_profile_{error_profile}.dat") + error_pattern = ERROR_PATTERNS_DIR.joinpath( + f"dly_error_profile_{error_profile}.dat" + ) else: raise ValueError( f"JBM profile number {error_profile} does not exist, should be between {LIST_JBM_PROFILES[0]} and {LIST_JBM_PROFILES[-1]}" @@ -190,6 +192,8 @@ def apply_network_simulator( n_frames_per_packet = 2 # apply error pattern - network_simulator(error_pattern, in_bitstream, out_bitstream, n_frames_per_packet, offset) + network_simulator( + error_pattern, in_bitstream, out_bitstream, n_frames_per_packet, offset + ) return diff --git a/ivas_processing_scripts/audiotools/wrappers/random_seed.py b/ivas_processing_scripts/audiotools/wrappers/random_seed.py index c7b8e057..802f68b9 100644 --- a/ivas_processing_scripts/audiotools/wrappers/random_seed.py +++ b/ivas_processing_scripts/audiotools/wrappers/random_seed.py @@ -31,6 +31,7 @@ # from typing import Optional + from ivas_processing_scripts.utils import find_binary, run @@ -70,7 +71,7 @@ def random_seed( str(prerun_seed), "-r", # value range for results str(0), - str(99999999) + str(99999999), ] # run command diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index 98193ea6..fc696d92 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -189,6 +189,32 @@ def get_processing_chain( cod_cfg = cond_cfg["cod"] dec_cfg = cond_cfg["dec"] + # Frame error pattern bitstream modification + if "tx" in cond_cfg.keys(): + tx_cfg = cond_cfg["tx"] + elif hasattr(cfg, "tx"): + if cfg.tx.get("type", None) == "FER": + tx_cfg = { + "type": cfg.tx.get("type", None), + "error_pattern": cfg.tx.get("error_pattern", None), + "error_rate": cfg.tx.get("error_rate", None), + "master_seed": cfg.master_seed, + "prerun_seed": cfg.prerun_seed, + } + elif cfg.tx.get("type", None) == "JBM": + tx_cfg = { + "type": cfg.tx.get("type", None), + "error_pattern": cfg.tx.get("error_pattern", None), + "error_profile": cfg.tx.get("error_profile", None), + "n_frames_per_packet": cfg.tx.get("n_frames_per_packet", 1), + } + else: + raise ValueError( + "Type of bitstream procesing either missing or not valid" + ) + else: + tx_cfg = None + chain["processes"].append( EVS( { @@ -201,6 +227,8 @@ def get_processing_chain( "dec_bin": dec_cfg.get("bin"), "dec_opts": dec_cfg.get("opts"), "multiprocessing": cfg.multiprocessing, + "tx": tx_cfg, + "preamble": cfg.preamble, } ) ) @@ -231,7 +259,9 @@ def get_processing_chain( "n_frames_per_packet": cfg.tx.get("n_frames_per_packet", 1), } else: - raise ValueError("Type of bitstream procesing either missing or not valid") + raise ValueError( + "Type of bitstream procesing either missing or not valid" + ) else: tx_cfg = None diff --git a/ivas_processing_scripts/processing/evs.py b/ivas_processing_scripts/processing/evs.py index 98cb5ce8..0fd2b245 100755 --- a/ivas_processing_scripts/processing/evs.py +++ b/ivas_processing_scripts/processing/evs.py @@ -31,18 +31,28 @@ # import logging +import os.path import platform from itertools import repeat from pathlib import Path from shutil import copyfile -from typing import Optional +from typing import Optional, Union from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import ( combine, parse_wave_header, + read, split_channels, ) +from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS +from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( + create_and_apply_error_pattern, +) +from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( + apply_network_simulator, + validate_network_simulator, +) from ivas_processing_scripts.processing.processing import Processing from ivas_processing_scripts.utils import apply_func_parallel, run @@ -158,7 +168,13 @@ class EVS(Processing): show_progress=False, ) - # TODO check eid-xor/networkSimulator here + split_chan_bs = apply_func_parallel( + self.simulate_tx, + zip(split_chan_files, split_chan_bs, repeat(logger)), + None, + "mt" if self.multiprocessing else None, + show_progress=False, + ) # run all decoders logger.debug(f"Running EVS decoders for {out_file.stem.split('.')[0]}") @@ -236,6 +252,60 @@ class EVS(Processing): run(cmd, logger=logger) + def simulate_tx( + self, + in_file: Union[Path, str], + bitstream: Path, + logger: Optional[logging.Logger] = None, + ) -> Union[Path, str]: + if self.tx is not None: + if self.tx["type"] == "JBM": + bs, ext = os.path.splitext(bitstream) + bitstream_processed = Path(f"{bs}_processed{ext}") + logger.debug(f"Network simulator {bitstream} -> {bitstream_processed}") + apply_network_simulator( + bitstream, + bitstream_processed, + self.tx["error_pattern"], + self.tx["error_profile"], + self.tx["n_frames_per_packet"], + ) + # add -voip cmdline option to the decoder + # TODO: tracefile also? + if self.dec_opts: + if "-voip" not in self.dec_opts: + self.dec_opts.extend(["-voip"]) + else: + self.dec_opts = ["-voip"] + return bitstream_processed + + elif self.tx["type"] == "FER": + bs, ext = os.path.splitext(bitstream) + bitstream_processed = Path(f"{bs}_processed{ext}") + signal, _ = read(in_file, fs=self.in_fs, nchannels=1) + frame_number = len(signal) / self.in_fs / IVAS_FRAME_LEN_MS * 1000 + if self.preamble: + frame_number_preamble = self.preamble / IVAS_FRAME_LEN_MS + else: + frame_number_preamble = 0 + logger.debug( + f"Frame loss simulator {bitstream} -> {bitstream_processed}" + ) + create_and_apply_error_pattern( + in_bitstream=bitstream, + out_bitstream=bitstream_processed, + len_sig=frame_number, + preamble=frame_number_preamble, + error_pattern=self.tx["error_pattern"], + error_rate=self.tx["error_rate"], + master_seed=self.tx["master_seed"], + prerun_seed=self.tx["prerun_seed"], + ) + + return bitstream_processed + else: + return bitstream + def dec( self, bitstream: Path, diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 3581d53c..b94f3cda 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -38,15 +38,16 @@ from typing import Optional, Union from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audiofile import parse_wave_header, read +from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( create_and_apply_error_pattern, ) from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( - apply_network_simulator, validate_network_simulator + apply_network_simulator, + validate_network_simulator, ) from ivas_processing_scripts.processing.processing import Processing from ivas_processing_scripts.utils import run -from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS class IVAS(Processing): @@ -82,9 +83,11 @@ class IVAS(Processing): # existence of error pattern files (if given) already here if self.tx is not None: if self.tx.get("type", None) == "JBM": - validate_network_simulator(self.tx["error_pattern"], - self.tx["error_profile"], - self.tx["n_frames_per_packet"]) + validate_network_simulator( + self.tx["error_pattern"], + self.tx["error_profile"], + self.tx["n_frames_per_packet"], + ) elif self.tx.get("type", None) == "FER": # TODO pass @@ -233,13 +236,17 @@ class IVAS(Processing): elif self.tx["type"] == "FER": bs, ext = os.path.splitext(bitstream) bitstream_processed = Path(f"{bs}_processed{ext}") - signal, _ = read(in_file, fs=self.in_fs, nchannels=self.in_fmt.num_channels) + signal, _ = read( + in_file, fs=self.in_fs, nchannels=self.in_fmt.num_channels + ) frame_number = len(signal) / self.in_fs / IVAS_FRAME_LEN_MS * 1000 if self.preamble: frame_number_preamble = self.preamble / IVAS_FRAME_LEN_MS else: frame_number_preamble = 0 - logger.debug(f"Frame loss simulator {bitstream} -> {bitstream_processed}") + logger.debug( + f"Frame loss simulator {bitstream} -> {bitstream_processed}" + ) create_and_apply_error_pattern( in_bitstream=bitstream, out_bitstream=bitstream_processed, diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index b50137ed..2e196a2c 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -116,9 +116,7 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): print(", ".join([str(sn) for sn in cfg.split_names]), file=f) print(", ".join([str(i.stem) for i in cfg.items_list]), file=f) - logger.info( - f"Splits written to file {cfg.concat_file.with_suffix('.splits.log')}" - ) + logger.info(f"Splits written to file {cfg.concat_file.with_suffix('.splits.log')}") def concat_teardown(cfg: TestConfig, logger: logging.Logger): -- GitLab From f3f3bcf02ca5936a1c1135e88b71e1fef75e4858 Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 17:32:41 +0200 Subject: [PATCH 30/31] added validation step for bitstream processing --- .../audiotools/wrappers/eid_xor.py | 45 +++++++++++++++++++ .../audiotools/wrappers/gen_patt.py | 4 +- .../audiotools/wrappers/networkSimulator.py | 1 - ivas_processing_scripts/processing/evs.py | 14 ++++++ ivas_processing_scripts/processing/ivas.py | 9 ++-- .../processing/processing.py | 6 +-- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py index 6d36ff22..72fb5ce2 100644 --- a/ivas_processing_scripts/audiotools/wrappers/eid_xor.py +++ b/ivas_processing_scripts/audiotools/wrappers/eid_xor.py @@ -30,6 +30,7 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # +import os.path from pathlib import Path from typing import Optional, Union @@ -139,3 +140,47 @@ def create_and_apply_error_pattern( eid_xor(error_pattern, in_bitstream, out_bitstream) return + + +def validate_error_pattern_application( + error_pattern: Optional[Union[Path, str]] = None, + error_rate: Optional[int] = None, +) -> None: + """ + Validate settings for the network simulator + + Parameters + ---------- + error_pattern: Optional[Union[Path, str]] + Path to existing error pattern + error_rate: Optional[int] + Frame error rate + """ + + if find_binary("gen-patt") is None: + raise FileNotFoundError( + "The binary gen-patt for error pattern generation was not found! Please check the configuration." + ) + if find_binary("eid-xor") is None: + raise FileNotFoundError( + "The binary eid-xor for error patter application was not found! Please check the configuration." + ) + if error_pattern is not None: + if not os.path.exists(os.path.realpath(error_pattern)): + raise FileNotFoundError( + f"The frame error profile file {error_pattern} was not found! Please check the configuration." + ) + if error_rate is not None: + raise ValueError( + "Frame error pattern and error rate are specified for bitstream processing. Can't use both! Please check the configuration." + ) + else: + if error_rate is None: + raise ValueError( + "Either error rate or error pattern has to be specified for FER bitstream processing." + ) + elif error_rate < 0 or error_rate > 100: + raise ValueError( + f"Specified error rate of {error_rate}% is either too large or too small." + ) + return diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index 71d8d93f..aa480af1 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -30,8 +30,6 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # -import shutil -import warnings from os import getcwd from pathlib import Path from tempfile import TemporaryDirectory @@ -129,7 +127,7 @@ def create_error_pattern( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - sta_file = ERROR_PATTERNS_DIR.joinpath(f"sta_template") + sta_file = ERROR_PATTERNS_DIR.joinpath("sta_template") tmp_sta_file = tmp_dir.joinpath("sta") # compute seed diff --git a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py index feb24891..fa1b7509 100644 --- a/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py +++ b/ivas_processing_scripts/audiotools/wrappers/networkSimulator.py @@ -34,7 +34,6 @@ import os.path from pathlib import Path from typing import Optional, Union -from ivas_processing_scripts.audiotools.wrappers.gen_patt import create_error_pattern from ivas_processing_scripts.utils import find_binary, run LIST_JBM_PROFILES = range(12) diff --git a/ivas_processing_scripts/processing/evs.py b/ivas_processing_scripts/processing/evs.py index 0fd2b245..ebe6ebf0 100755 --- a/ivas_processing_scripts/processing/evs.py +++ b/ivas_processing_scripts/processing/evs.py @@ -48,6 +48,7 @@ from ivas_processing_scripts.audiotools.audiofile import ( from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( create_and_apply_error_pattern, + validate_error_pattern_application, ) from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( apply_network_simulator, @@ -99,6 +100,19 @@ class EVS(Processing): self.bitrate.extend( [self.bitrate[-1]] * (self.in_fmt.num_channels - len(self.bitrate)) ) + # existence of error pattern files (if given) already here + if self.tx is not None: + if self.tx.get("type", None) == "JBM": + validate_network_simulator( + self.tx["error_pattern"], + self.tx["error_profile"], + self.tx["n_frames_per_packet"], + ) + elif self.tx.get("type", None) == "FER": + validate_error_pattern_application( + self.tx["error_pattern"], + self.tx["error_rate"], + ) def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index b94f3cda..ddfbc207 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -41,6 +41,7 @@ from ivas_processing_scripts.audiotools.audiofile import parse_wave_header, read from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS from ivas_processing_scripts.audiotools.wrappers.eid_xor import ( create_and_apply_error_pattern, + validate_error_pattern_application, ) from ivas_processing_scripts.audiotools.wrappers.networkSimulator import ( apply_network_simulator, @@ -79,7 +80,7 @@ class IVAS(Processing): raise FileNotFoundError( "The IVAS decoder binary was not found! Please check the configuration." ) - # TODO: if tx_fer or tx_jbm test for existence of bitstream processing binaries and + # existence of error pattern files (if given) already here if self.tx is not None: if self.tx.get("type", None) == "JBM": @@ -89,8 +90,10 @@ class IVAS(Processing): self.tx["n_frames_per_packet"], ) elif self.tx.get("type", None) == "FER": - # TODO - pass + validate_error_pattern_application( + self.tx["error_pattern"], + self.tx["error_rate"], + ) def process( self, in_file: Path, out_file: Path, in_meta, logger: logging.Logger diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 2e196a2c..a6c68e42 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -36,7 +36,6 @@ from itertools import repeat from pathlib import Path from shutil import copyfile from typing import Iterable, Union -from warnings import warn from ivas_processing_scripts.audiotools.audiofile import concat, split from ivas_processing_scripts.audiotools.metadata import ( @@ -120,9 +119,8 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): def concat_teardown(cfg: TestConfig, logger: logging.Logger): - try: - num_splits = len(cfg.splits) - except AttributeError: + + if not cfg.splits: raise ValueError("Splitting not possible without split marker") output_format = cfg.postprocessing["fmt"] -- GitLab From aed8c36ecd1f40e676b9aaa44f53a9f8d49a250c Mon Sep 17 00:00:00 2001 From: Treffehn Date: Wed, 19 Apr 2023 17:41:24 +0200 Subject: [PATCH 31/31] very small formatting --- ivas_processing_scripts/audiotools/audiofile.py | 2 -- ivas_processing_scripts/processing/processing.py | 1 - 2 files changed, 3 deletions(-) diff --git a/ivas_processing_scripts/audiotools/audiofile.py b/ivas_processing_scripts/audiotools/audiofile.py index e33b0d57..f8fad48f 100755 --- a/ivas_processing_scripts/audiotools/audiofile.py +++ b/ivas_processing_scripts/audiotools/audiofile.py @@ -38,8 +38,6 @@ from typing import Optional, Tuple, Union import numpy as np import scipy.io.wavfile as wav -from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS - from .audioarray import trim, window logger = logging.getLogger("__main__") diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index a6c68e42..d0b80a76 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -119,7 +119,6 @@ def concat_setup(cfg: TestConfig, logger: logging.Logger): def concat_teardown(cfg: TestConfig, logger: logging.Logger): - if not cfg.splits: raise ValueError("Splitting not possible without split marker") -- GitLab