Source code for crowsetta.formats.seq.textgrid.classes

"""Data classes used to represent components of TextGrids.
"""
from __future__ import annotations

import numpy as np
from attrs import define, field


[docs] def valid_time(instance, attribute, value): if not value >= 0.0: raise ValueError(f"{attribute} is a time and must be a non-negative number but was: {value}")
[docs] @define class Interval: """Class representing an interval in an interval tier from a Praat TextGrid. Attributes ---------- xmin: float Start time of interval, in seconds. xmax: float End time of interval, in seconds. text: str Label for interval. See Also -------- :class:`~crowsetta.formats.seq.textgrid.classes.IntervalTier`. """ xmin: float = field(validator=valid_time) xmax: float = field(validator=valid_time) text: str def __attrs_post_init__(self): if self.xmax < self.xmin: raise ValueError(f"xmax must be greater than xmin but xmax was {self.xmax} and xmin was {self.xmin}")
[docs] @define class IntervalTier: """Class representing an *interval tier* in a Praat TextGrid. As described in the Praat documentation[1]_: An interval tier is a connected sequence of labelled intervals, with boundaries in between. Attributes ---------- name: str A name given to the interval tier, e.g. "phonemes". xmin: float Start time of interval tier, in seconds. xmax: float End time of interval tier, in seconds. intervals: list A list of :class:`~crowsetta.formats.seq.textgrid.classes.Interval` instances. See Also -------- :class:`~crowsetta.formats.seq.textgrid.classes.Interval` References ---------- .. [^1]: https://www.fon.hum.uva.nl/praat/manual/TextGrid.html """ name: str xmin: float = field(validator=valid_time) xmax: float = field(validator=valid_time) intervals: list[Interval] def __attrs_post_init__(self): if self.xmax < self.xmin: raise ValueError(f"xmax must be greater than xmin but xmax was {self.xmax} and xmin was {self.xmin}") # sort because (1) we want them in ascending order of xmin and # (2) we use this to check for overlap self.intervals = sorted(self.intervals, key=lambda interval: interval.xmin) xmax_lt_all_xmin = [] for ind in range(len(self.intervals) - 1): xmax_lt_all_xmin.append( all([self.intervals[ind].xmax <= interval.xmin for interval in self.intervals[ind + 1 :]]) # noqa: E203 ) if not all(xmax_lt_all_xmin): have_overlap = [(ind, self.intervals[ind]) for ind in np.nonzero(xmax_lt_all_xmin)[0]] err_str = "" for has_overlap_ind, interval in have_overlap: overlaps_with = [ (overlaps_with_ind, self.intervals[overlaps_with_ind]) for overlaps_with_ind in range(has_overlap_ind + 1, len(self.intervals) - 1) if not (self.intervals[has_overlap_ind].xmax <= self.intervals[overlaps_with_ind].xmin) ] err_str += ( f"Interval {has_overlap_ind} with xmin {interval.xmin} and xmax {interval.xmax} overlaps with " ) for overlaps_with_ind, interval in overlaps_with: err_str += f"interval {overlaps_with_ind} with xmin {interval.xmin} and xmax {interval.xmax}, " err_str = err_str[:-2] + ".\n" raise ValueError( "TextGrids with overlapping intervals are not valid.\n" "Found the following overlapping intervals:\n" f"{err_str}" ) def __iter__(self): return iter(self.intervals)
[docs] @define class Point: """Class representing a point in a point tier from a Praat TextGrid. Attributes ---------- number: float Time of point, in seconds. mark: str Label for point. See Also -------- :class:`~crowsetta.formats.seq.textgrid.classes.PointTier`. """ number: float = field(validator=valid_time) mark: str
[docs] @define class PointTier: """Class representing a *point tier* in a Praat TextGrid. As described in the Praat documentation[1]_: A point tier is a sequence of labelled points. Attributes ---------- name: str A name given to the point tier, e.g. "stimulus onset". xmin: float Start time of IntervalTier, in seconds. xmax: float End time of IntervalTier, in seconds. points: list A list of :class:`~crowsetta.formats.seq.textgrid.classes.Point` instances. See Also -------- :class:`~crowsetta.formats.seq.textgrid.classes.Point` References ---------- .. [^1]: https://www.fon.hum.uva.nl/praat/manual/TextGrid.html """ name: str xmin: float = field(validator=valid_time) xmax: float = field(validator=valid_time) points: list[Point] def __attrs_post_init__(self): if self.xmax < self.xmin: raise ValueError(f"xmax must be greater than xmin but xmax was {self.xmax} and xmin was {self.xmin}") def __iter__(self): return iter(self.points)