cut *MDF* file. *start* and *stop* limits are absolute values
        or values relative to the first timestamp depending on the *whence*
        argument.

        Parameters
        ----------
        start : float
            start time, default *None*. If *None* then the start of measurement
            is used
        stop : float
            stop time, default *None*. If *None* then the end of measurement is
            used
        whence : int
            how to search for the start and stop values

            * 0 : absolute
            * 1 : relative to first timestamp
        version : str
            new mdf file version from ('2.00', '2.10', '2.14', '3.00', '3.10',
            '3.20', '3.30', '4.00', '4.10', '4.11'); default *None* and in this
            case the original file version is used
        include_ends : bool
            include the *start* and *stop* timestamps after cutting the signal.
            If *start* and *stop* are found in the original timestamps, then
            the new samples will be computed using interpolation. Default *True*
        time_from_zero : bool
            start time stamps from 0s in the cut measurement

        Returns
        -------
        out : MDF
            new MDF object

def cut(
        self,
        start=None,
        stop=None,
        whence=0,
        version=None,
        include_ends=True,
        time_from_zero=False,
    ):
        """cut *MDF* file. *start* and *stop* limits are absolute values
        or values relative to the first timestamp depending on the *whence*
        argument.

        Parameters
        ----------
        start : float
            start time, default *None*. If *None* then the start of measurement
            is used
        stop : float
            stop time, default *None*. If *None* then the end of measurement is
            used
        whence : int
            how to search for the start and stop values

            * 0 : absolute
            * 1 : relative to first timestamp
        version : str
            new mdf file version from ('2.00', '2.10', '2.14', '3.00', '3.10',
            '3.20', '3.30', '4.00', '4.10', '4.11'); default *None* and in this
            case the original file version is used
        include_ends : bool
            include the *start* and *stop* timestamps after cutting the signal.
            If *start* and *stop* are found in the original timestamps, then
            the new samples will be computed using interpolation. Default *True*
        time_from_zero : bool
            start time stamps from 0s in the cut measurement

        Returns
        -------
        out : MDF
            new MDF object

        """

        if version is None:
            version = self.version
        else:
            version = validate_version_argument(version)

        out = MDF(version=version)

        if whence == 1:
            timestamps = []
            for i, group in enumerate(self.groups):
                fragment = next(self._load_data(group))
                master = self.get_master(
                    i,
                    record_offset=0,
                    record_count=1,
                )
                if master.size:
                    timestamps.append(master[0])
            self._master_channel_cache.clear()

            if timestamps:
                first_timestamp = np.amin(timestamps)
            else:
                first_timestamp = 0

            if start is not None:
                start += first_timestamp
            if stop is not None:
                stop += first_timestamp

        if time_from_zero:
            delta = start
            t_epoch = self.header.start_time.timestamp() + delta
            out.header.start_time = datetime.fromtimestamp(t_epoch)
        else:
            delta = 0
            out.header.start_time = self.header.start_time

        groups_nr = len(self.groups)

        if self._callback:
            self._callback(0, groups_nr)

        cg_nr = -1

        interpolation_mode = self._integer_interpolation

        # walk through all groups and get all channels
        for i, group in enumerate(self.groups):
            included_channels = self._included_channels(i)
            if included_channels:
                cg_nr += 1
            else:
                continue

            data = self._load_data(group)
            parents, dtypes = self._prepare_record(group)

            idx = 0
            for fragment in data:
                if dtypes.itemsize:
                    group.record = np.core.records.fromstring(fragment[0], dtype=dtypes)
                else:
                    group.record = None
                master = self.get_master(i, fragment, copy_master=False)
                if not len(master):
                    continue

                needs_cutting = True

                # check if this fragement is within the cut interval or
                # if the cut interval has ended
                if start is None and stop is None:
                    fragment_start = None
                    fragment_stop = None
                    start_index = 0
                    stop_index = len(master)
                    needs_cutting = False
                elif start is None:
                    fragment_start = None
                    start_index = 0
                    if master[0] > stop:
                        break
                    else:
                        fragment_stop = min(stop, master[-1])
                        stop_index = np.searchsorted(
                            master, fragment_stop, side="right"
                        )
                        if stop_index == len(master):
                            needs_cutting = False
                elif stop is None:
                    fragment_stop = None
                    if master[-1] < start:
                        continue
                    else:
                        fragment_start = max(start, master[0])
                        start_index = np.searchsorted(
                            master, fragment_start, side="left"
                        )
                        stop_index = len(master)
                        if start_index == 0:
                            needs_cutting = False
                else:
                    if master[0] > stop:
                        break
                    elif master[-1] < start:
                        continue
                    else:
                        fragment_start = max(start, master[0])
                        start_index = np.searchsorted(
                            master, fragment_start, side="left"
                        )
                        fragment_stop = min(stop, master[-1])
                        stop_index = np.searchsorted(
                            master, fragment_stop, side="right"
                        )
                        if start_index == 0 and stop_index == len(master):
                            needs_cutting = False

                if needs_cutting:
                    cut_timebase = (
                        Signal(master, master, name="_")
                        .cut(fragment_start, fragment_stop, include_ends, interpolation_mode=interpolation_mode)
                        .timestamps
                    )

                # the first fragment triggers and append that will add the
                # metadata for all channels
                if idx == 0:
                    sigs = []
                    for j in included_channels:
                        sig = self.get(
                            group=i,
                            index=j,
                            data=fragment,
                            raw=True,
                            ignore_invalidation_bits=True,
                            copy_master=False,
                        )
                        if needs_cutting:
                            sig = sig.interp(cut_timebase, interpolation_mode=interpolation_mode)

                        # if sig.stream_sync and False:
                        #     attachment, _name = sig.attachment
                        #     duration = get_video_stream_duration(attachment)
                        #     if start is None:
                        #         start_t = 0
                        #     else:
                        #         start_t = start
                        #
                        #     if stop is None:
                        #         end_t = duration
                        #     else:
                        #         end_t = stop
                        #
                        #     attachment = cut_video_stream(
                        #         attachment,
                        #         start_t,
                        #         end_t,
                        #         Path(_name).suffix,
                        #     )
                        #     sig.attachment = attachment, _name

                        if not sig.samples.flags.writeable:
                            sig.samples = sig.samples.copy()
                        sigs.append(sig)

                    if sigs:
                        if time_from_zero:
                            new_timestamps = cut_timebase - delta
                            for sig in sigs:
                                sig.timestamps = new_timestamps
                        if start:
                            start_ = f"{start}s"
                        else:
                            start_ = "start of measurement"
                        if stop:
                            stop_ = f"{stop}s"
                        else:
                            stop_ = "end of measurement"
                        out.append(
                            sigs, f"Cut from {start_} to {stop_}", common_timebase=True
                        )
                        try:
                            if group.channel_group.flags & v4c.FLAG_CG_BUS_EVENT:
                                out.groups[-1].channel_group.flags = group.channel_group.flags
                                out.groups[-1].channel_group.acq_name = group.channel_group.acq_name
                                out.groups[-1].channel_group.acq_source = group.channel_group.acq_source
                                out.groups[-1].channel_group.comment = group.channel_group.comment
                        except AttributeError:
                            pass
                    else:
                        break

                    idx += 1

                # the other fragments will trigger onl the extension of
                # samples records to the data block
                else:
                    if needs_cutting:
                        timestamps = cut_timebase
                    else:
                        timestamps = master

                    if time_from_zero:
                        timestamps = timestamps - delta

                    sigs = [(timestamps, None)]

                    for j in included_channels:
                        sig = self.get(
                            group=i,
                            index=j,
                            data=fragment,
                            raw=True,
                            samples_only=True,
                            ignore_invalidation_bits=True,
                        )
                        if needs_cutting:
                            _sig = Signal(
                                sig[0], master, name="_", invalidation_bits=sig[1]
                            ).interp(cut_timebase, interpolation_mode=interpolation_mode)
                            sig = (_sig.samples, _sig.invalidation_bits)

                            del _sig
                        sigs.append(sig)

                    if sigs:
                        out.extend(cg_nr, sigs)

                    idx += 1

                group.record = None

            # if the cut interval is not found in the measurement
            # then append an empty data group
            if idx == 0:

                self.configure(read_fragment_size=1)
                sigs = []

                fragment = next(self._load_data(group))

                fragment = (fragment[0], -1, None)

                for j in included_channels:
                    sig = self.get(
                        group=i,
                        index=j,
                        data=fragment,
                        raw=True,
                        ignore_invalidation_bits=True,
                    )
                    sig.samples = sig.samples[:0]
                    sig.timestamps = sig.timestamps[:0]
                    sigs.append(sig)

                if start:
                    start_ = f"{start}s"
                else:
                    start_ = "start of measurement"
                if stop:
                    stop_ = f"{stop}s"
                else:
                    stop_ = "end of measurement"
                out.append(sigs, f"Cut from {start_} to {stop_}", common_timebase=True)

                self.configure(read_fragment_size=0)

            if self._callback:
                self._callback(i + 1, groups_nr)

            if self._terminate:
                return

        out._transfer_events(self)
        if self._callback:
            out._callback = out._mdf._callback = self._callback
        return out