Commit ddd3ae98 authored by Archit Tamarapu's avatar Archit Tamarapu
Browse files

[fix] refactor tracefile processing and correctly undo repeat_signal and...

[fix] refactor tracefile processing and correctly undo repeat_signal and preamble for JBM conditions
parent 94506ae5
Loading
Loading
Loading
Loading
+32 −55
Original line number Diff line number Diff line
@@ -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):
+16 −13
Original line number Diff line number Diff line
@@ -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


+56 −0
Original line number Diff line number Diff line
@@ -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