From 7726611e54ec690d8e942d70e241609ddce11a41 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 29 Jul 2025 17:55:21 +0200 Subject: [PATCH 1/6] rename function related to metadata only to avoid confusion --- ivas_processing_scripts/audiotools/metadata.py | 4 ++-- ivas_processing_scripts/processing/preprocessing_2.py | 4 ++-- ivas_processing_scripts/processing/processing.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index 6eb1199a..89ec0dbe 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -376,7 +376,7 @@ def concat_meta_from_file( # add preamble if preamble: - concat_meta_all_obj = add_remove_preamble(concat_meta_all_obj, preamble) + concat_meta_all_obj = add_remove_metadata_preamble(concat_meta_all_obj, preamble) write_ISM_metadata_in_file(concat_meta_all_obj, out_file) @@ -621,7 +621,7 @@ def metadata_search_MASA( return list_meta -def add_remove_preamble( +def add_remove_metadata_preamble( metadata, preamble, add: Optional[bool] = True, diff --git a/ivas_processing_scripts/processing/preprocessing_2.py b/ivas_processing_scripts/processing/preprocessing_2.py index 646dc3cb..cce42bbf 100644 --- a/ivas_processing_scripts/processing/preprocessing_2.py +++ b/ivas_processing_scripts/processing/preprocessing_2.py @@ -40,7 +40,7 @@ from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audioarray import trim from ivas_processing_scripts.audiotools.audiofile import write from ivas_processing_scripts.audiotools.metadata import ( - add_remove_preamble, + add_remove_metadata_preamble, write_ISM_metadata_in_file, ) from ivas_processing_scripts.audiotools.wrappers.bs1770 import ( @@ -83,7 +83,7 @@ class Preprocessing2(Processing): metadata = audio_object.object_pos # add preamble - metadata = add_remove_preamble(metadata, preamble) + metadata = add_remove_metadata_preamble(metadata, preamble) # repeat signal if self.repeat_signal: diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index ca4c6940..e86d6eae 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -49,7 +49,7 @@ from ivas_processing_scripts.audiotools.audiofile import concat, trim from ivas_processing_scripts.audiotools.constants import IVAS_FRAME_LEN_MS from ivas_processing_scripts.audiotools.convert.__init__ import convert from ivas_processing_scripts.audiotools.metadata import ( - add_remove_preamble, + add_remove_metadata_preamble, concat_meta_from_file, ) from ivas_processing_scripts.constants import LOGGER_DATEFMT, LOGGER_FORMAT @@ -604,7 +604,7 @@ def remove_preamble(x, out_fmt, fs, repeat_signal, preamble_len_ms, meta, logger # remove preamble if preamble_len_ms > 0: - meta = add_remove_preamble(meta, preamble_len_ms, add=False) + meta = add_remove_metadata_preamble(meta, preamble_len_ms, add=False) # cut first half of signal if repeat_signal: -- GitLab From 94506ae5138f6fbfab16a3b160a19b799fb70f41 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 29 Jul 2025 17:56:05 +0200 Subject: [PATCH 2/6] [fix] typos in TEMPLATE.yml --- examples/TEMPLATE.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/TEMPLATE.yml b/examples/TEMPLATE.yml index 72fcd82c..6248a888 100755 --- a/examples/TEMPLATE.yml +++ b/examples/TEMPLATE.yml @@ -37,7 +37,7 @@ input_path: ".../ivas/items/HOA3" output_path: ".../temp_output" ### Metadata path or file(s) ### If input format is ISM{1-4} a path for the metadata files can be specified; -### default = null (for ISM search for item_name.{wav, raw, pcm}.{0-3}.csv in input folder, otherise ignored) +### default = null (for ISM search for item_name.{wav, raw, pcm}.{0-3}.csv in input folder, otherwise ignored) # metadata_path: ### Path can be set for all items with the 'all_items' key (automatic search for item_name.{wav, raw, pcm}.{0-3}.csv within this folder) # all_items: ".../metadata_folder" @@ -115,7 +115,7 @@ input: ### Horizontally concatenate input items into one long file; default = false # concatenate_input: true ### if concatenation is applied, the following two keys can be used to add zeros before or after the items - ### duration is specified in miliseconds + ### duration is specified in milliseconds # silence_pre: 2000 # silence_post: 2000 ### Specify the concatenation order in a list of strings. If not specified, the concatenation order would be @@ -126,7 +126,7 @@ input: # concatenation_order: [] ### Specify preamble duration in ms; default = 0 # preamble: 10000 - ### Flag wheter to use noise (amplitude +-4) for the preamble or silence; default = false (silence) + ### Flag whether to use noise (amplitude +-4) for the preamble or silence; default = false (silence) # preamble_noise: true ### Additive background noise # background_noise: @@ -154,7 +154,7 @@ input: ### REQUIRED: either error_pattern (and errpatt_late_loss_rate or errpatt_delay) or error_profile ### delay error profile file # error_pattern: ".../dly_error_profile.dat" - ### Late loss rate in precent for EVS + ### Late loss rate in percent for EVS # errpatt_late_loss_rate: 1 ### Constant JBM delay in milliseconds for EVS # errpatt_delay: 200 -- GitLab From ddd3ae98e5a1ce5d908aa4f7d8427a6ada6b21a6 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 29 Jul 2025 17:58:15 +0200 Subject: [PATCH 3/6] [fix] refactor tracefile processing and correctly undo repeat_signal and preamble for JBM conditions --- .../processing/processing.py | 87 +++++++------------ .../processing_splitting_scaling.py | 29 ++++--- ivas_processing_scripts/processing/tx.py | 56 ++++++++++++ 3 files changed, 104 insertions(+), 68 deletions(-) diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index e86d6eae..60282a8d 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -34,10 +34,8 @@ import logging import sys from abc import ABC, abstractmethod from itertools import repeat -from multiprocessing import Pool from pathlib import Path from shutil import copyfile -from time import sleep from typing import Iterable, Union from warnings import warn @@ -202,7 +200,17 @@ def concat_setup(cfg: TestConfig, chain, logger: logging.Logger): def concat_teardown( - x, splits, out_fmt, fs, in_fs, meta, tracefile, ivas_jbm, logger: logging.Logger + x, + splits, + out_fmt, + fs, + in_fs, + meta, + tracefile, + ivas_jbm, + repeat_signal, + preamble, + logger: logging.Logger, ): if splits is None: raise ValueError("Splitting not possible without split marker") @@ -213,7 +221,7 @@ def concat_teardown( if (out_fmt.startswith("ISM") or out_fmt.startswith("MASA")) and ivas_jbm: raise ValueError( - "Splitting with JBM compensation not supportet for formats with metadata (e.g. MASA, ISM)" + "Splitting with JBM compensation not supported for formats with metadata (e.g. MASA, ISM)" ) if logger and ivas_jbm: @@ -228,59 +236,28 @@ def concat_teardown( relative_fs_change = fs_new / fs_old new_splits = [0] for split_i in splits: - new_splits.append(int(float(split_i) * relative_fs_change)) + new_splits.append(int(split_i * relative_fs_change)) splits = new_splits else: - # adjust splits for jbm ivas conditions - # following code is based on jbmtrim.cpp script - rtpTimeScale = 1000 # in ms - playTimeScale = 1000 # in ms - new_splits = [None] * (len(splits) + 1) - - split_start = 1 / float(fs) - i = 0 - lastRtpTs = 0 - lastPlayTime = 0 - # find last JBM trace entry with lower or equal RTP time stamp - for j in range(tracefile.shape[0]): - entry = tracefile[j] - # ignore frames with unknown RTP time stamp or playout time - if entry[1] == -1 or entry[3] < 0: - continue - # check if the next position to cut is found - if entry[1] / rtpTimeScale >= split_start: - # interpolate between current and previous RTP time stamp to - # increase accuracy in case of DTX where lot of time stamps are missing - if (num := entry[1] / rtpTimeScale - split_start) == 0: - rtpTsRelErr = num - else: - rtpTsRelErr = num / ( - ((entry[1] - lastRtpTs) / rtpTimeScale) + sys.float_info.epsilon - ) - playTimeAbsErr = rtpTsRelErr * (entry[3] - lastPlayTime) / playTimeScale - # found one split, save in list and search for next - new_splits[i] = entry[3] / playTimeScale - playTimeAbsErr - # get next split marker; add one to make computation more similar to jbmtrim - split_start = (float(splits[i]) + 1) / float(fs) - i += 1 - if i >= len(new_splits): - break - lastRtpTs = entry[1] - lastPlayTime = entry[3] - - # check if all splits are found - if i < (len(new_splits) - 1): - raise ValueError("Error in item splitting with JBM compensation") - elif i < (len(new_splits)): - # catch item with missing end - warn("Last split after end of file for IVAS JBM condition") - new_splits[i] = lastPlayTime / playTimeScale - - # set new values and use new sampling rate - splits = new_splits - for s in range(len(splits)): - # subtract one again (was only used to make computation more similar to jbmtrim) - splits[s] = int(np.floor(splits[s] * float(in_fs))) - 1 + # handle signal repeat and preamble for JBM conditions since we have trace data + + # for preamble, the first split point should be at the end of the preamble + preamble_smp = preamble * in_fs // 1000 if preamble > 0 else 0 + # for repeat signal, the first split point should be the start of the repetition + repeat_start = preamble_smp + splits[-1] if repeat_signal else 0 + + # adjust the offsets of the splits accordingly + splits = [ + preamble_smp + repeat_start, + *[s + preamble_smp + repeat_start for s in splits], + ] + + splits_ts = get_timescaled_splits(tracefile, splits, fs, in_fs) + + # the above function assumes the starting point is the first playout frame + # for repeat signal or preamble, we don't want the split to start there + if repeat_signal or preamble > 0: + splits = splits_ts[1:] # check if last split ending coincides with last sample of signal if splits[-1] > len(x): diff --git a/ivas_processing_scripts/processing/processing_splitting_scaling.py b/ivas_processing_scripts/processing/processing_splitting_scaling.py index 2280f203..597e256a 100644 --- a/ivas_processing_scripts/processing/processing_splitting_scaling.py +++ b/ivas_processing_scripts/processing/processing_splitting_scaling.py @@ -203,8 +203,17 @@ class Processing_splitting_scaling(Processing): def revert_preamble_concatenation( self, x, fs, in_file, out_file, in_meta, noerror=False, logger=None ): + if self.ivas_jbm and not noerror: + # read JBM data + tracefile = in_file.with_name(f"{in_file.name.split('.')[0]}.tracefile.csv") + tracefile_data = np.genfromtxt(tracefile, delimiter=";") + validate_tracefile(tracefile_data) + else: + tracefile_data = None + # remove preamble and first half of signal due to repetition - if self.preamble or self.repeat_signal: + # JBM conditions are handled later in concat_teardown + if not self.ivas_jbm and (self.preamble or self.repeat_signal): x, in_meta = remove_preamble( x, self.out_fmt, @@ -215,7 +224,7 @@ class Processing_splitting_scaling(Processing): logger, ) - # reverse concatenation + # reverse concatenation, also handles repeat_signal/preamble reversal for JBM if self.concatenate_input: # read out splits file -> start/end, names, sampling rate splits_info_file = Path( @@ -224,15 +233,6 @@ class Processing_splitting_scaling(Processing): ) ) splits, split_names, split_fs = read_splits_file(splits_info_file) - if self.ivas_jbm and not noerror: - # read out tracefile with jbm info - tracefile_info_file = Path( - f"{in_file.with_suffix('').with_suffix('')}.tracefile.csv" - ) - tracefile_info = np.genfromtxt(tracefile_info_file, delimiter=";") - validate_tracefile(tracefile_info) - else: - tracefile_info = None # split file if self.ivas_jbm and not noerror: @@ -248,8 +248,10 @@ class Processing_splitting_scaling(Processing): fs, split_fs, in_meta, - tracefile_info, + tracefile_data, ivas_jbm_splitting_flag, + self.repeat_signal, + self.preamble, logger, ) @@ -311,8 +313,9 @@ def read_splits_file(splits_file): """Read out splitting information from split log in preproceesing 2 temp folder""" with open(splits_file, "r") as f: splits = f.readline()[:-1].split(", ") + splits = [int(s) for s in splits] names = f.readline()[:-1].split(", ") - fs = f.readline()[:-1] + fs = int(f.readline()[:-1]) return splits, names, fs diff --git a/ivas_processing_scripts/processing/tx.py b/ivas_processing_scripts/processing/tx.py index e8f3f954..3ec929cb 100755 --- a/ivas_processing_scripts/processing/tx.py +++ b/ivas_processing_scripts/processing/tx.py @@ -30,7 +30,11 @@ # the United Nations Convention on Contracts on the International Sales of Goods. # +import sys from typing import Optional +from warnings import warn + +import numpy as np from ivas_processing_scripts.processing.config import TestConfig from ivas_processing_scripts.utils import get_abs_path @@ -70,3 +74,55 @@ def get_tx_cfg(cfg: TestConfig, cond_cfg: dict, is_EVS: bool = False) -> Optiona raise ValueError("Type of bitstream procesing either missing or not valid") return tx_cfg + + +def get_timescaled_splits(tracefile, splits, out_fs, in_fs): + # adjust splits for jbm ivas conditions + # following code is based on jbmtrim.cpp script + rtpTimeScale = 1000 # in ms + playTimeScale = 1000 # in ms + new_splits = [None] * (len(splits) + 1) + + split_start = 1 / float(out_fs) + i = 0 + lastRtpTs = 0 + lastPlayTime = 0 + # find last JBM trace entry with lower or equal RTP time stamp + for entry in tracefile: + # ignore frames with unknown RTP time stamp or playout time + if entry[1] == -1 or entry[3] < 0: + continue + # check if the next position to cut is found + if entry[1] / rtpTimeScale >= split_start: + # interpolate between current and previous RTP time stamp to + # increase accuracy in case of DTX where lot of time stamps are missing + if (num := entry[1] / rtpTimeScale - split_start) == 0: + rtpTsRelErr = num + else: + rtpTsRelErr = num / ( + ((entry[1] - lastRtpTs) / rtpTimeScale) + sys.float_info.epsilon + ) + playTimeAbsErr = rtpTsRelErr * (entry[3] - lastPlayTime) / playTimeScale + # found one split, save in list and search for next + new_splits[i] = entry[3] / playTimeScale - playTimeAbsErr + # get next split marker; add one to make computation more similar to jbmtrim + split_start = (splits[i] + 1) / float(out_fs) + i += 1 + if i >= len(new_splits): + break + lastRtpTs = entry[1] + lastPlayTime = entry[3] + + # check if all splits are found + if i < (len(new_splits) - 1): + raise ValueError("Error in item splitting with JBM compensation") + elif i < (len(new_splits)): + # catch item with missing end + warn("Last split after end of file for IVAS JBM condition") + new_splits[i] = lastPlayTime / playTimeScale + + # set new values and use new sampling rate + # subtract one again (was only used to make computation more similar to jbmtrim) + new_splits = [int(np.floor(s * float(in_fs))) - 1 for s in new_splits] + + return new_splits -- GitLab From 6a473f304864c4b00bfe58dc1963ec39bea2ba68 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 29 Jul 2025 17:59:22 +0200 Subject: [PATCH 4/6] [fix] multiprocessing setting in config not respected in some processing stages --- ivas_processing_scripts/__init__.py | 30 ++----------- .../processing/processing.py | 42 +++---------------- 2 files changed, 9 insertions(+), 63 deletions(-) diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index f36bdc9a..a462d3b4 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -190,33 +190,9 @@ def main(args): (item, tmp_dir, out_dir, chain["processes"], logger, metadata) ) - if cfg.multiprocessing: - # set up values for progress display and chunksize - count = len(item_args) - width = 80 - - # submit tasks to the pool - p = Pool() - results = p.starmap_async( - process_item, - item_args, - ) - - # poll progress - progressbar_update(0, count, width) - while not results.ready(): - progressbar_update(count - int(results._number_left), count, width) - spinner() - sleep(0.1) - progressbar_update(count, count, width) - print("", flush=True, file=sys.stdout) - results.get() - - p.close() - p.join() - - else: - apply_func_parallel(process_item, item_args, None, None, True) + apply_func_parallel( + process_item, item_args, None, "mp" if cfg.multiprocessing else None, True + ) # copy configuration to output directory cfg.to_file(cfg.output_path.joinpath(f"{cfg.name}.yml")) diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 60282a8d..90e2767c 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -52,11 +52,11 @@ from ivas_processing_scripts.audiotools.metadata import ( ) from ivas_processing_scripts.constants import LOGGER_DATEFMT, LOGGER_FORMAT from ivas_processing_scripts.processing.config import TestConfig +from ivas_processing_scripts.processing.tx import get_timescaled_splits from ivas_processing_scripts.utils import ( + apply_func_parallel, list_audio, pairwise, - progressbar_update, - spinner, ) @@ -323,25 +323,10 @@ def preprocess(cfg, logger): cfg.metadata_path, ) ) - p = Pool() - results = p.starmap_async( - process_item, - args, + apply_func_parallel( + process_item, args, None, "mp" if cfg.multiprocessing else None, True ) - # poll progress - progressbar_update(0, count, width) - while not results.ready(): - progressbar_update(count - int(results._number_left), count, width) - spinner() - sleep(0.1) - progressbar_update(count, count, width) - print("", flush=True, file=sys.stdout) - results.get() - - p.close() - p.join() - # update the configuration to use preprocessing outputs as new inputs cfg.items_list = list_audio( cfg.out_dirs[0], select_list=getattr(cfg, "input_select", None) @@ -419,25 +404,10 @@ def preprocess_2(cfg, logger): cfg.metadata_path, ) ) - p = Pool() - results = p.starmap_async( - process_item, - args, + apply_func_parallel( + process_item, args, None, "mp" if cfg.multiprocessing else None, True ) - # poll progress - progressbar_update(0, count, width) - while not results.ready(): - progressbar_update(count - int(results._number_left), count, width) - spinner() - sleep(0.1) - progressbar_update(count, count, width) - print("", flush=True, file=sys.stdout) - results.get() - - p.close() - p.join() - # update the configuration to use preprocessing 2 outputs as new inputs cfg.items_list = list_audio( cfg.out_dirs[0], select_list=getattr(cfg, "input_select", None) -- GitLab From 4a753ea65814bec794d38ff9abbc133084d25796 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 29 Jul 2025 18:01:39 +0200 Subject: [PATCH 5/6] formatting + remove unused variables --- ivas_processing_scripts/audiotools/metadata.py | 4 +++- .../processing/processing.py | 17 +---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/ivas_processing_scripts/audiotools/metadata.py b/ivas_processing_scripts/audiotools/metadata.py index 89ec0dbe..8bcd4662 100755 --- a/ivas_processing_scripts/audiotools/metadata.py +++ b/ivas_processing_scripts/audiotools/metadata.py @@ -376,7 +376,9 @@ def concat_meta_from_file( # add preamble if preamble: - concat_meta_all_obj = add_remove_metadata_preamble(concat_meta_all_obj, preamble) + concat_meta_all_obj = add_remove_metadata_preamble( + concat_meta_all_obj, preamble + ) write_ISM_metadata_in_file(concat_meta_all_obj, out_file) diff --git a/ivas_processing_scripts/processing/processing.py b/ivas_processing_scripts/processing/processing.py index 90e2767c..51380988 100755 --- a/ivas_processing_scripts/processing/processing.py +++ b/ivas_processing_scripts/processing/processing.py @@ -31,7 +31,6 @@ # import logging -import sys from abc import ABC, abstractmethod from itertools import repeat from pathlib import Path @@ -39,8 +38,6 @@ from shutil import copyfile from typing import Iterable, Union from warnings import warn -import numpy as np - from ivas_processing_scripts.audiotools import audio from ivas_processing_scripts.audiotools.audioarray import window from ivas_processing_scripts.audiotools.audiofile import concat, trim @@ -53,11 +50,7 @@ from ivas_processing_scripts.audiotools.metadata import ( from ivas_processing_scripts.constants import LOGGER_DATEFMT, LOGGER_FORMAT from ivas_processing_scripts.processing.config import TestConfig from ivas_processing_scripts.processing.tx import get_timescaled_splits -from ivas_processing_scripts.utils import ( - apply_func_parallel, - list_audio, - pairwise, -) +from ivas_processing_scripts.utils import apply_func_parallel, list_audio, pairwise class Processing(ABC): @@ -308,10 +301,6 @@ def preprocess(cfg, logger): logger.info(f" Generating condition: {preprocessing['name']}") - # set up values for progress display and chunksize - count = len(cfg.items_list) - width = 80 - # run preprocessing args = list( zip( @@ -389,10 +378,6 @@ def preprocess_2(cfg, logger): if chain[0].concatenate_input: concat_setup(cfg, chain, logger) - # set up values for progress display and chunksize - count = len(cfg.items_list) - width = 80 - # run preprocessing 2 args = list( zip( -- GitLab From 09f530de8975e0119ad71ba2257915e6d96538f1 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 31 Jul 2025 11:30:57 +0200 Subject: [PATCH 6/6] lint + format --- ivas_processing_scripts/__init__.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ivas_processing_scripts/__init__.py b/ivas_processing_scripts/__init__.py index a462d3b4..a457eb5e 100755 --- a/ivas_processing_scripts/__init__.py +++ b/ivas_processing_scripts/__init__.py @@ -31,10 +31,7 @@ # import logging -import sys from itertools import product -from multiprocessing import Pool -from time import sleep from ivas_processing_scripts.audiotools.metadata import ( check_ISM_metadata, @@ -53,12 +50,7 @@ from ivas_processing_scripts.processing.processing import ( process_item, reorder_items_list, ) -from ivas_processing_scripts.utils import ( - DirManager, - apply_func_parallel, - progressbar_update, - spinner, -) +from ivas_processing_scripts.utils import DirManager, apply_func_parallel def logging_init(args, cfg): -- GitLab