diff --git a/README.md b/README.md index 07f1e541071e2f0c2c4d3263b2b88a2c7511a38f..990df6780f4013076d83e4b2230793d27a3c06dd 100755 --- a/README.md +++ b/README.md @@ -386,6 +386,8 @@ conditions_to_generate: bitrates: # - 9600 - [13200, 13200, 8000, 13200, 9600] + ### for multi-channel configs, code LFE with 9.6 kbps NB (as mandated by IVAS-3) + evs_lfe_9k6bps_nb: true cod: ### Path to encoder binary; default search for IVAS_cod in bin folder (primary) and PATH (secondary) bin: ~/git/ivas-codec/IVAS_cod @@ -468,6 +470,7 @@ This configuration has to match the channel configuration. If the provided list 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`. +The additional key `evs_lfe_9k6bps_nb` is only available for EVS conditions and ensures a bitrate of 9.6kbps and narrow band processing of the LFE channel(s). #### 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`. diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 47f6a195247227dfb2cf0e07eba5722ad86c6e06..1968aee55821c43a6fc39781735d4eab66b4a36b 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -247,11 +247,15 @@ conditions_to_generate: bitrates: # - 9600 - [13200, 13200, 8000, 13200, 9600] + ### for multi-channel configs, code LFE with 9.6 kbps NB (as mandated by IVAS-3) + evs_lfe_9k6bps_nb: true + ### Encoder options cod: ### Path to encoder binary; default search for IVAS_cod in bin folder (primary) and PATH (secondary) bin: ~/git/ivas-codec/IVAS_cod ### Encoder input sampling rate in Hz (resampling performed in case of mismatch); default = null (no resampling) # fs: 32000 + ### Decoder options dec: ### Path to encoder binary; default search for IVAS_dec in bin folder (primary) and PATH (secondary) bin: ~/git/ivas-codec/IVAS_dec diff --git a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py index f801b07b9e3edbab53831f1606477e7d06e7c764..d8737ef408e0e48690363aefc019b5cf34c8ee3c 100644 --- a/ivas_processing_scripts/audiotools/wrappers/gen_patt.py +++ b/ivas_processing_scripts/audiotools/wrappers/gen_patt.py @@ -138,7 +138,7 @@ def create_error_pattern( gen_patt(100, "ep.g192", 5, working_dir=tmp_dir_test) if not tmp_sta_file_test.exists(): raise RuntimeError( - "Used version of gen-patt was detected to be faulty (unable to write \"sta\"-file). See bin/README.md for details." + 'Used version of gen-patt was detected to be faulty (unable to write "sta"-file). See bin/README.md for details.' ) with TemporaryDirectory() as tmp_dir: diff --git a/ivas_processing_scripts/processing/chains.py b/ivas_processing_scripts/processing/chains.py index 027ac4472b2e6310a4a8fca6ceaed5893a20f6ec..0eb913a054559b31362a39dc3e237ba94d1b6e01 100755 --- a/ivas_processing_scripts/processing/chains.py +++ b/ivas_processing_scripts/processing/chains.py @@ -32,6 +32,7 @@ from pathlib import Path from typing import Optional +from warnings import warn from ivas_processing_scripts.processing.config import TestConfig from ivas_processing_scripts.processing.evs import EVS @@ -242,6 +243,9 @@ def get_processing_chain( cod_cfg = cond_cfg["cod"] dec_cfg = cond_cfg["dec"] + # 9.6 kbit/s NB for EVS LFE coding + evs_lfe_9k6bps_nb = cond_cfg.get("evs_lfe_9k6bps_nb", None) + # Frame error pattern bitstream modification if "tx" in cond_cfg.keys() or hasattr(cfg, "tx"): # local specification overwrites global one @@ -295,6 +299,7 @@ def get_processing_chain( "multiprocessing": cfg.multiprocessing, "tx": tx_cfg, "preamble": preamble, + "evs_lfe_9k6bps_nb": evs_lfe_9k6bps_nb, } ) ) @@ -305,6 +310,14 @@ def get_processing_chain( cod_cfg = cond_cfg["cod"] dec_cfg = cond_cfg["dec"] + # 9.6 kbit/s NB for EVS LFE coding only applies to EVS conditions + evs_lfe_9k6bps_nb = cond_cfg.get("evs_lfe_9k6bps_nb", None) + + if evs_lfe_9k6bps_nb is not None: + warn( + "Option evs_lfe_9k6bps_nb only applicable to evs conditions - ignoring for ivas." + ) + # Frame error pattern bitstream modification if "tx" in cond_cfg.keys() or hasattr(cfg, "tx"): # local specification overwrites global one diff --git a/ivas_processing_scripts/processing/evs.py b/ivas_processing_scripts/processing/evs.py index ebe6ebf0ef6440382f3226e4424668ffeb244a85..7c52eaabe42846925dab651736d77d8901ffa82d 100755 --- a/ivas_processing_scripts/processing/evs.py +++ b/ivas_processing_scripts/processing/evs.py @@ -100,6 +100,17 @@ class EVS(Processing): self.bitrate.extend( [self.bitrate[-1]] * (self.in_fmt.num_channels - len(self.bitrate)) ) + + # special handling for LFE: check, whether evs_lfe_9k6bps_nb is true in multi-channel configs + self.proc_chan_lfe_9k6bps_nb = [False] * self.in_fmt.num_channels + # if channel-based audio, determine the LFE channels + if isinstance(self.in_fmt, audio.ChannelBasedAudio): + if self.in_fmt.lfe_index is not None and self.evs_lfe_9k6bps_nb is True: + lfe_index = self.in_fmt.lfe_index + for i in lfe_index: + self.bitrate[i] = 9600 + self.proc_chan_lfe_9k6bps_nb[i] = True + # existence of error pattern files (if given) already here if self.tx is not None: if self.tx.get("type", None) == "JBM": @@ -176,7 +187,13 @@ class EVS(Processing): logger.debug(f"Running EVS encoders for {out_file.stem.split('.')[0]}") apply_func_parallel( self.enc, - zip(split_chan_files, split_chan_bs, self.bitrate, repeat(logger)), + zip( + split_chan_files, + split_chan_bs, + self.bitrate, + self.proc_chan_lfe_9k6bps_nb, + repeat(logger), + ), None, "mt" if self.multiprocessing else None, show_progress=False, @@ -248,12 +265,30 @@ class EVS(Processing): in_pcm_file: Path, bitstream: Path, bitrate: int, + proc_chan_lfe_9k6bps_nb: bool, logger: Optional[logging.Logger] = None, ) -> None: cmd = [self.cod_bin] - if self.cod_opts: - cmd.extend(self.cod_opts) + # in case of LFE 9.6 kbps NB processing strip any "-max_band XX" from cmd.extend and amend by "-max_band NB" + if proc_chan_lfe_9k6bps_nb is True: + if self.cod_opts: + cod_opts = self.cod_opts + # for finding index of max_band entry use lower case version + cod_opts_lower = [x.lower() for x in self.cod_opts] + if "-max_band" in cod_opts_lower: + idx_max_band = cod_opts_lower.index("-max_band") + # use original version and not lower case version due to possible paths in there + cod_opts[idx_max_band + 1] = "NB" + else: + cod_opts = ["-max_band", "NB"] + else: + if self.cod_opts: + cod_opts = self.cod_opts + else: + cod_opts = [] + + cmd.extend(cod_opts) cmd.extend( [