diff --git a/README.md b/README.md index ad6ccdf6474c776c71a7c16177d363922a65390b..3178d8fc4ad008af141ee924d131a7a49803f80d 100755 --- a/README.md +++ b/README.md @@ -554,6 +554,11 @@ The configuration of the IVAS condition is similar to the EVS condition. However In addition to that, the encoder and decoder take some additional arguments defined by the key `opts`. For the decoder an output format can be set. If this argument is not defined the format specified in postprocessing is used. +### IVAS External Renderer + +The key `ivas_rend` can be added in each condition to apply the IVAS_rend external renderer after the condition (e.g. after encoding and decoding for `ivas`) but previous to the +postprocessing. + --- ## Supported audio formats diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index bab59220e02885565d29df5c25aa71385b6d5d35..7537005639f65d3fe45a09eb12f2f64e5e92048c 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -175,6 +175,14 @@ conditions_to_generate: type: ref ### optional low-pass cut-off frequency in Hz; default = null # out_fc: 22500 + ### optional use of IVAS_rend (can be used in all conditions) + # ivas_rend: + ### Path to renderer binary; default search for IVAS_rend in bin folder (primary) and PATH (secondary) + # bin: ~/git/ivas-codec/IVAS_rend + ### Additional commandline options; default = null + # opts: [] + ### Renderer output format; default = postprocessing fmt + # fmt: "BINAURAL" c02: ### REQUIRED: type of condition type: lp3k5 @@ -218,6 +226,14 @@ conditions_to_generate: # fs: 48000 ### Additional commandline options; default = null # opts: ["-q", "-no_delay_cmp"] + ### optional use of IVAS_rend (can be used in all conditions) + # ivas_rend: + ### Path to renderer binary; default search for IVAS_rend in bin folder (primary) and PATH (secondary) + # bin: ~/git/ivas-codec/IVAS_rend + ### Additional commandline options; default = null + # opts: [] + ### Renderer output format; default = postprocessing fmt + # fmt: "BINAURAL" ### Bitstream options # tx: ### For possible arguments see overall bitstream modification diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index f9ec2c7928fe7570f379b24f2b4e799b299ab47e..340b35ea27ddc3a3d034a5edf2c731f2ab945091 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -39,7 +39,7 @@ from ivas_processing_scripts.audiotools.audioarray import trim from ivas_processing_scripts.audiotools.audiofile import read, write from ivas_processing_scripts.processing.config import TestConfig from ivas_processing_scripts.processing.evs import EVS -from ivas_processing_scripts.processing.ivas import IVAS +from ivas_processing_scripts.processing.ivas import IVAS, IVAS_rend from ivas_processing_scripts.processing.postprocessing import Postprocessing from ivas_processing_scripts.processing.preprocessing import Preprocessing from ivas_processing_scripts.processing.preprocessing_2 import Preprocessing2 @@ -450,6 +450,25 @@ def get_processing_chain( else: raise SystemExit(f"Unknown condition {condition}!") + # add optional IVAS_rend rendering step after each condition + if cond_cfg.get("ivas_rend", -1) != -1: + rend_cfg = cond_cfg["ivas_rend"] + chain["processes"].append( + IVAS_rend( + { + "in_fmt": tmp_in_fmt, + "in_fs": tmp_in_fs, + "out_fmt": rend_cfg.get("fmt", tmp_out_fmt), + "bin": get_abs_path(rend_cfg.get("bin", None)), + "opts": rend_cfg.get("opts"), + "use_windows_codec_binaries": cfg.use_windows_codec_binaries, + } + ) + ) + # update values to reflect renderer output + tmp_in_fs = rend_cfg.get("fs", tmp_in_fs) + tmp_in_fmt = rend_cfg.get("fmt", tmp_out_fmt) + # add postprocessing step based on condition post_fmt = post_cfg.get("fmt") if isinstance(post_fmt, list): diff --git a/ivas_processing_scripts/processing/config.py b/ivas_processing_scripts/processing/config.py index d813f766d5ef411609717d18e93f403c5e1af6ff..25cabe02f810af644e7105be48597cffe0ba0963 100755 --- a/ivas_processing_scripts/processing/config.py +++ b/ivas_processing_scripts/processing/config.py @@ -79,6 +79,17 @@ def get_default_config_for_codecs(codec_name: str, ext_with_dot: str = "") -> di return cfg +def get_default_config_for_renderer(codec_name: str, ext_with_dot: str = "") -> dict: + rend_bin = f"{codec_name}_rend{ext_with_dot}" + + cfg = { + "ivas_rend": { + "bin": find_binary(rend_bin, raise_error=False), + }, + } + return cfg + + class TestConfig: # avoid confusion with pytest tests due to naming __test__ = False @@ -244,6 +255,12 @@ class TestConfig: f"The following key must be specified for ESDRU: {REQUIRED_KEYS_ESDRU}" ) + if cond_cfg.get("ivas_rend", -1) != -1: + merged_cfg = get_default_config_for_renderer("IVAS", codec_bin_extension) + merge_dicts(merged_cfg, cond_cfg) + cfg["conditions_to_generate"][cond_name] = merged_cfg + + def _validate_merged_config(self, cfg: dict): # if not on windows, but "use_windows_codec_binaries" is given, assure that wine is there if ( diff --git a/ivas_processing_scripts/processing/ivas.py b/ivas_processing_scripts/processing/ivas.py index 217f47ad87cbc723dcab2d7304f9102845e3ca9f..12f569c29bf44020a60624084ac314dc01290946 100755 --- a/ivas_processing_scripts/processing/ivas.py +++ b/ivas_processing_scripts/processing/ivas.py @@ -110,9 +110,8 @@ class IVAS(Processing): # set defaults in case input and output sampling rates were not specified if not self.in_fs: if in_file.suffix in [".pcm", ".raw"]: - self.in_fs = 48000 + raise ValueError("Sampling rate has to be specified for headerless files!") elif in_file.suffix == ".txt": - # TODO raise NotImplementedError(".txt file support is WIP!") else: self.in_fs = parse_wave_header(in_file)["fs"] @@ -366,12 +365,25 @@ class IVAS_rend(Processing): self.name = "ivas_rend" self.in_fmt = audio.fromtype(self.in_fmt) self.out_fmt = audio.fromtype(self.out_fmt) + if not hasattr(self, "opts"): + self.dec_opts = None + self._use_wine = ( + platform.system() == "Linux" and self.use_windows_codec_binaries + ) def _validate(self): + need_exe_suffix = ( + platform.system() == "Windows" or self.use_windows_codec_binaries + ) if not Path(self.bin).exists(): - raise FileNotFoundError( - f"The IVAS renderer binary was not found at the given path: {self.cod_bin}" - ) + if need_exe_suffix and ( + self.bin and Path(self.bin).with_suffix(".exe").exists() + ): + self.bin = Path(self.bin).with_suffix(".exe") + else: + raise FileNotFoundError( + "The IVAS renderer binary was not found! Please check the configuration." + ) def process( self, @@ -380,46 +392,38 @@ class IVAS_rend(Processing): in_meta, logger: Optional[logging.Logger] = None, ) -> None: - # set defaults in case input and output sampling rates were not specified + logger.debug(f"IVAS rend configuration : {self.__dict__}") + logger.debug(f"IVAS rend {in_file.absolute()} -> {out_file.absolute()}") + + # set defaults in case input sampling rate is not specified if not self.in_fs: if in_file.suffix in [".pcm", ".raw"]: - self.in_fs = 48000 + raise ValueError("Sampling rate has to be specified for headerless files!") elif in_file.suffix == ".txt": - # TODO raise NotImplementedError(".txt file support is WIP!") else: - self.in_fs = parse_wave_header(in_file)["fs"] - - if not self.out_fs: - self.out_fs = self.in_fs - - raise NotImplementedError("IVAS Renderer support is WIP!") + self.in_fs = parse_wave_header(str(in_file))["fs"] cmd = [self.bin] - if self.fs: - cmd.extend(["-fs", str(self.fs // 1000)]) - - if self.metadata: - cmd.append("-im") - cmd.extend([str(f) for f in self.metadata]) - - if self.trajectory: - cmd.extend(["-tf", str(self.trajectory)]) + if self._use_wine: + cmd.insert(0, "wine") cmd.extend( [ - "-q", - "-i", - str(in_file), - "-if", - str(self.in_fmt.name), - "-o", - str(out_file), - "-of", - str(self.out_fmt.name), + "-fs", str(self.in_fs // 1000), + "-i", str(in_file), + "-if", self.in_fmt.name, + "-o", str(out_file), + "-of", self.out_fmt.name, ] ) + if in_meta: + cmd.append("-im") + cmd.extend([str(f) for f in in_meta]) + if self.opts: cmd.extend(self.opts) + + run(cmd, logger=logger)