praatio.data_classes.klattgrid

KlattGrid and related classes.

KlattGrids can be used for synthesizing and manipulating speech

  1"""KlattGrid and related classes.
  2
  3KlattGrids can be used for synthesizing and manipulating speech
  4"""
  5import io
  6
  7from typing import List, Optional, Dict, Callable, Union
  8
  9from praatio.data_classes import textgrid
 10from praatio.data_classes import textgrid_tier
 11from praatio.utilities import errors
 12
 13
 14class _KlattBaseTier:
 15    def __init__(self, name: str):
 16        self.tierNameList: List[str] = []  # Preserves the order of the tiers
 17        self.tierDict: Dict[str, "_KlattBaseTier"] = {}
 18        self.name = name
 19        self.minTimestamp = None
 20        self.maxTimestamp = None
 21
 22    def __eq__(self, other):
 23        if not isinstance(other, _KlattBaseTier):
 24            return False
 25
 26        isEqual = True
 27        isEqual &= self.name == other.name
 28        isEqual &= self.minTimestamp == other.minTimestamp
 29        isEqual &= self.maxTimestamp == other.maxTimestamp
 30
 31        isEqual &= self.tierNameList == other.tierNameList
 32        if isEqual:
 33            for tierName in self.tierNameList:
 34                isEqual &= self.tierDict[tierName] == other.tierDict[tierName]
 35
 36        return isEqual
 37
 38    def addTier(self, tier, tierIndex=None) -> None:
 39
 40        if tierIndex is None:
 41            self.tierNameList.append(tier.name)
 42        else:
 43            self.tierNameList.insert(tierIndex, tier.name)
 44
 45        if tier.name in list(self.tierDict.keys()):
 46            raise errors.TierNameExistsError(
 47                f"Cannot add tier with name {tier.name} as it already exists in the Klattgrid"
 48            )
 49        self.tierDict[tier.name] = tier
 50
 51        minV = tier.minTimestamp
 52        if self.minTimestamp is None or (minV is not None and minV < self.minTimestamp):
 53            self.minTimestamp = minV
 54
 55        maxV = tier.maxTimestamp
 56        if self.maxTimestamp is None or (maxV is not None and maxV > self.maxTimestamp):
 57            self.maxTimestamp = maxV
 58
 59
 60class KlattContainerTier(_KlattBaseTier):
 61    """Contains a set of intermediate tiers"""
 62
 63    def getAsText(self):
 64        outputTxt = ""
 65        outputTxt += "%s? <exists>\n" % self.name
 66
 67        try:
 68            self.minTimestamp = toIntOrFloat(self.minTimestamp)
 69            outputTxt += "xmin = %s\nxmax = %s\n" % (
 70                repr(self.minTimestamp),
 71                repr(self.maxTimestamp),
 72            )
 73        except TypeError:
 74            pass
 75
 76        for name in self.tierNameList:
 77            outputTxt += self.tierDict[name].getAsText()
 78
 79        return outputTxt
 80
 81    def modifySubtiers(self, tierName: str, modFunc) -> None:
 82        """
 83        Modify values in every tier contained in the named intermediate tier
 84        """
 85        kit = self.tierDict[tierName]
 86        for name in kit.tierNameList:
 87            subpointTier = kit.tierDict[name]
 88            subpointTier.modifyValues(modFunc)
 89
 90
 91class KlattIntermediateTier(_KlattBaseTier):
 92    """
 93    Has many point tiers that are semantically related (e.g. formant tiers)
 94    """
 95
 96    def getAsText(self):
 97        outputTxt = ""
 98        headerTxt = "%s: size=%d\n" % (self.name, len(self.tierNameList))
 99
100        for name in self.tierNameList:
101            outputTxt += self.tierDict[name].getAsText()
102
103        outputTxt = headerTxt + outputTxt
104
105        return outputTxt
106
107
108class KlattPointTier(textgrid_tier.TextgridTier):
109    """
110    A Klatt tier not contained within another tier
111    """
112
113    def __init__(
114        self,
115        name: str,
116        entries: List,
117        minT: Optional[float] = None,
118        maxT: Optional[float] = None,
119    ):
120
121        entries = [(float(time), label) for time, label in entries]
122
123        # Determine the min and max timestamps
124        timeList = [time for time, label in entries]
125        if minT is not None:
126            timeList.append(float(minT))
127        if maxT is not None:
128            timeList.append(float(maxT))
129
130        try:
131            setMinT = min(timeList)
132            setMaxT = max(timeList)
133        except ValueError:
134            raise errors.TimelessTextgridTierException()
135
136        super(KlattPointTier, self).__init__(name, entries, setMinT, setMaxT)
137
138    def crop(self):
139        raise NotImplementedError
140
141    def dejitter(self):
142        raise NotImplementedError
143
144    def deleteEntry(self, entry):
145        raise NotImplementedError
146
147    def editTimestamps(self):
148        raise NotImplementedError
149
150    def eraseRegion(self):
151        raise NotImplementedError
152
153    def insertEntry(self):
154        raise NotImplementedError
155
156    def insertSpace(self):
157        raise NotImplementedError
158
159    @property
160    def timestamps(self):
161        raise NotImplementedError
162
163    def validate(self):
164        raise NotImplementedError
165
166    def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
167        newEntries = [
168            (timestamp, modFunc(float(value))) for timestamp, value in self.entries
169        ]
170
171        self._entries = newEntries
172
173    def getAsText(self) -> str:
174        outputList = []
175        self.minTimestamp = toIntOrFloat(self.minTimestamp)
176        outputList.append("%s? <exists> " % self.name)
177        outputList.append("xmin = %s" % repr(self.minTimestamp))
178        outputList.append("xmax = %s" % repr(self.maxTimestamp))
179
180        if self.name not in ["phonation", "vocalTract", "coupling", "frication"]:
181            outputList.append("points: size= %d" % len(self.entries))
182
183        for i, entry in enumerate(self.entries):
184            outputList.append("points [%d]:" % (i + 1))
185            outputList.append("    number = %s" % repr(entry[0]))
186            outputList.append("    value = %s" % repr(entry[1]))
187
188        return "\n".join(outputList) + "\n"
189
190
191class KlattSubPointTier(KlattPointTier):
192    """
193    Tiers contained in a KlattIntermediateTier
194    """
195
196    def getAsText(self) -> str:
197        outputList = []
198        outputList.append("%s:" % self.name)
199        self.minTimestamp = toIntOrFloat(self.minTimestamp)
200        outputList.append("    xmin = %s" % repr(self.minTimestamp))
201        outputList.append("    xmax = %s" % repr(self.maxTimestamp))
202        outputList.append("    points: size = %d" % len(self.entries))
203
204        for i, entry in enumerate(self.entries):
205            outputList.append("    points [%d]:" % (i + 1))
206            outputList.append("        number = %s" % repr(entry[0]))
207            outputList.append("        value = %s" % repr(entry[1]))
208
209        return "\n".join(outputList) + "\n"
210
211
212class Klattgrid(textgrid.Textgrid):
213    def save(self, fn: str, minimumIntervalLength: Optional[float] = None):
214        """
215
216        minimumIntervalLength is used for compatibility with Textgrid.save()
217            but it has no impact on a Klattgrid
218        """
219
220        # Header
221        outputTxt = ""
222        outputTxt += 'File type = "ooTextFile"\n'
223        outputTxt += 'Object class = "KlattGrid"\n\n'
224        self.minTimestamp = toIntOrFloat(self.minTimestamp)
225        outputTxt += "xmin = %s\nxmax = %s\n" % (
226            repr(self.minTimestamp),
227            repr(self.maxTimestamp),
228        )
229
230        for tierName in self.tierNames:
231            outputTxt += self._tierDict[tierName].getAsText()
232
233        outputTxt = _cleanNumericValues(outputTxt)
234
235        with io.open(fn, "w", encoding="utf-8") as fd:
236            fd.write(outputTxt)
237
238
239def toIntOrFloat(val: Union[str, float]) -> float:
240    if float(val) - float(int(val)) == 0.0:
241        val = int(val)
242    else:
243        val = float(val)
244    return val
245
246
247def _cleanNumericValues(dataStr: str) -> str:
248    dataList = dataStr.split("\n")
249    newDataList = []
250    for row in dataList:
251        row = row.rstrip()
252        try:
253            if "min" in row or "max" in row:
254                raise errors.ParsingError(
255                    f"Found unexpected keyword 'min' or 'max' in row '{row}'"
256                )
257
258            head, tail = row.split("=")
259            head = head.rstrip()
260            tail = tail.strip()
261            try:
262                row = str(int(tail))
263            except ValueError:
264                tail = "%s" % tail
265                if float(tail) == 0:
266                    tail = "0"
267            row = "%s = %s" % (head, tail)
268        except (ValueError, errors.ParsingError):  # TODO: Is it really ok?
269            pass
270        finally:
271            newDataList.append(row.rstrip())
272
273    outputTxt = "\n".join(newDataList)
274
275    return outputTxt
class KlattContainerTier(_KlattBaseTier):
61class KlattContainerTier(_KlattBaseTier):
62    """Contains a set of intermediate tiers"""
63
64    def getAsText(self):
65        outputTxt = ""
66        outputTxt += "%s? <exists>\n" % self.name
67
68        try:
69            self.minTimestamp = toIntOrFloat(self.minTimestamp)
70            outputTxt += "xmin = %s\nxmax = %s\n" % (
71                repr(self.minTimestamp),
72                repr(self.maxTimestamp),
73            )
74        except TypeError:
75            pass
76
77        for name in self.tierNameList:
78            outputTxt += self.tierDict[name].getAsText()
79
80        return outputTxt
81
82    def modifySubtiers(self, tierName: str, modFunc) -> None:
83        """
84        Modify values in every tier contained in the named intermediate tier
85        """
86        kit = self.tierDict[tierName]
87        for name in kit.tierNameList:
88            subpointTier = kit.tierDict[name]
89            subpointTier.modifyValues(modFunc)

Contains a set of intermediate tiers

def getAsText(self):
64    def getAsText(self):
65        outputTxt = ""
66        outputTxt += "%s? <exists>\n" % self.name
67
68        try:
69            self.minTimestamp = toIntOrFloat(self.minTimestamp)
70            outputTxt += "xmin = %s\nxmax = %s\n" % (
71                repr(self.minTimestamp),
72                repr(self.maxTimestamp),
73            )
74        except TypeError:
75            pass
76
77        for name in self.tierNameList:
78            outputTxt += self.tierDict[name].getAsText()
79
80        return outputTxt
def modifySubtiers(self, tierName: str, modFunc) -> None:
82    def modifySubtiers(self, tierName: str, modFunc) -> None:
83        """
84        Modify values in every tier contained in the named intermediate tier
85        """
86        kit = self.tierDict[tierName]
87        for name in kit.tierNameList:
88            subpointTier = kit.tierDict[name]
89            subpointTier.modifyValues(modFunc)

Modify values in every tier contained in the named intermediate tier

class KlattIntermediateTier(_KlattBaseTier):
 92class KlattIntermediateTier(_KlattBaseTier):
 93    """
 94    Has many point tiers that are semantically related (e.g. formant tiers)
 95    """
 96
 97    def getAsText(self):
 98        outputTxt = ""
 99        headerTxt = "%s: size=%d\n" % (self.name, len(self.tierNameList))
100
101        for name in self.tierNameList:
102            outputTxt += self.tierDict[name].getAsText()
103
104        outputTxt = headerTxt + outputTxt
105
106        return outputTxt

Has many point tiers that are semantically related (e.g. formant tiers)

def getAsText(self):
 97    def getAsText(self):
 98        outputTxt = ""
 99        headerTxt = "%s: size=%d\n" % (self.name, len(self.tierNameList))
100
101        for name in self.tierNameList:
102            outputTxt += self.tierDict[name].getAsText()
103
104        outputTxt = headerTxt + outputTxt
105
106        return outputTxt
class KlattPointTier(praatio.data_classes.textgrid_tier.TextgridTier):
109class KlattPointTier(textgrid_tier.TextgridTier):
110    """
111    A Klatt tier not contained within another tier
112    """
113
114    def __init__(
115        self,
116        name: str,
117        entries: List,
118        minT: Optional[float] = None,
119        maxT: Optional[float] = None,
120    ):
121
122        entries = [(float(time), label) for time, label in entries]
123
124        # Determine the min and max timestamps
125        timeList = [time for time, label in entries]
126        if minT is not None:
127            timeList.append(float(minT))
128        if maxT is not None:
129            timeList.append(float(maxT))
130
131        try:
132            setMinT = min(timeList)
133            setMaxT = max(timeList)
134        except ValueError:
135            raise errors.TimelessTextgridTierException()
136
137        super(KlattPointTier, self).__init__(name, entries, setMinT, setMaxT)
138
139    def crop(self):
140        raise NotImplementedError
141
142    def dejitter(self):
143        raise NotImplementedError
144
145    def deleteEntry(self, entry):
146        raise NotImplementedError
147
148    def editTimestamps(self):
149        raise NotImplementedError
150
151    def eraseRegion(self):
152        raise NotImplementedError
153
154    def insertEntry(self):
155        raise NotImplementedError
156
157    def insertSpace(self):
158        raise NotImplementedError
159
160    @property
161    def timestamps(self):
162        raise NotImplementedError
163
164    def validate(self):
165        raise NotImplementedError
166
167    def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
168        newEntries = [
169            (timestamp, modFunc(float(value))) for timestamp, value in self.entries
170        ]
171
172        self._entries = newEntries
173
174    def getAsText(self) -> str:
175        outputList = []
176        self.minTimestamp = toIntOrFloat(self.minTimestamp)
177        outputList.append("%s? <exists> " % self.name)
178        outputList.append("xmin = %s" % repr(self.minTimestamp))
179        outputList.append("xmax = %s" % repr(self.maxTimestamp))
180
181        if self.name not in ["phonation", "vocalTract", "coupling", "frication"]:
182            outputList.append("points: size= %d" % len(self.entries))
183
184        for i, entry in enumerate(self.entries):
185            outputList.append("points [%d]:" % (i + 1))
186            outputList.append("    number = %s" % repr(entry[0]))
187            outputList.append("    value = %s" % repr(entry[1]))
188
189        return "\n".join(outputList) + "\n"

A Klatt tier not contained within another tier

KlattPointTier( name: str, entries: List, minT: Optional[float] = None, maxT: Optional[float] = None)
114    def __init__(
115        self,
116        name: str,
117        entries: List,
118        minT: Optional[float] = None,
119        maxT: Optional[float] = None,
120    ):
121
122        entries = [(float(time), label) for time, label in entries]
123
124        # Determine the min and max timestamps
125        timeList = [time for time, label in entries]
126        if minT is not None:
127            timeList.append(float(minT))
128        if maxT is not None:
129            timeList.append(float(maxT))
130
131        try:
132            setMinT = min(timeList)
133            setMaxT = max(timeList)
134        except ValueError:
135            raise errors.TimelessTextgridTierException()
136
137        super(KlattPointTier, self).__init__(name, entries, setMinT, setMaxT)

A container that stores and operates over interval and point tiers

def crop(self):
139    def crop(self):
140        raise NotImplementedError
def dejitter(self):
142    def dejitter(self):
143        raise NotImplementedError
def deleteEntry(self, entry):
145    def deleteEntry(self, entry):
146        raise NotImplementedError
def editTimestamps(self):
148    def editTimestamps(self):
149        raise NotImplementedError
def eraseRegion(self):
151    def eraseRegion(self):
152        raise NotImplementedError
def insertEntry(self):
154    def insertEntry(self):
155        raise NotImplementedError
def insertSpace(self):
157    def insertSpace(self):
158        raise NotImplementedError
timestamps
def validate(self):
164    def validate(self):
165        raise NotImplementedError
def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
167    def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
168        newEntries = [
169            (timestamp, modFunc(float(value))) for timestamp, value in self.entries
170        ]
171
172        self._entries = newEntries
def getAsText(self) -> str:
174    def getAsText(self) -> str:
175        outputList = []
176        self.minTimestamp = toIntOrFloat(self.minTimestamp)
177        outputList.append("%s? <exists> " % self.name)
178        outputList.append("xmin = %s" % repr(self.minTimestamp))
179        outputList.append("xmax = %s" % repr(self.maxTimestamp))
180
181        if self.name not in ["phonation", "vocalTract", "coupling", "frication"]:
182            outputList.append("points: size= %d" % len(self.entries))
183
184        for i, entry in enumerate(self.entries):
185            outputList.append("points [%d]:" % (i + 1))
186            outputList.append("    number = %s" % repr(entry[0]))
187            outputList.append("    value = %s" % repr(entry[1]))
188
189        return "\n".join(outputList) + "\n"
class KlattSubPointTier(KlattPointTier):
192class KlattSubPointTier(KlattPointTier):
193    """
194    Tiers contained in a KlattIntermediateTier
195    """
196
197    def getAsText(self) -> str:
198        outputList = []
199        outputList.append("%s:" % self.name)
200        self.minTimestamp = toIntOrFloat(self.minTimestamp)
201        outputList.append("    xmin = %s" % repr(self.minTimestamp))
202        outputList.append("    xmax = %s" % repr(self.maxTimestamp))
203        outputList.append("    points: size = %d" % len(self.entries))
204
205        for i, entry in enumerate(self.entries):
206            outputList.append("    points [%d]:" % (i + 1))
207            outputList.append("        number = %s" % repr(entry[0]))
208            outputList.append("        value = %s" % repr(entry[1]))
209
210        return "\n".join(outputList) + "\n"

Tiers contained in a KlattIntermediateTier

def getAsText(self) -> str:
197    def getAsText(self) -> str:
198        outputList = []
199        outputList.append("%s:" % self.name)
200        self.minTimestamp = toIntOrFloat(self.minTimestamp)
201        outputList.append("    xmin = %s" % repr(self.minTimestamp))
202        outputList.append("    xmax = %s" % repr(self.maxTimestamp))
203        outputList.append("    points: size = %d" % len(self.entries))
204
205        for i, entry in enumerate(self.entries):
206            outputList.append("    points [%d]:" % (i + 1))
207            outputList.append("        number = %s" % repr(entry[0]))
208            outputList.append("        value = %s" % repr(entry[1]))
209
210        return "\n".join(outputList) + "\n"
class Klattgrid(praatio.data_classes.textgrid.Textgrid):
213class Klattgrid(textgrid.Textgrid):
214    def save(self, fn: str, minimumIntervalLength: Optional[float] = None):
215        """
216
217        minimumIntervalLength is used for compatibility with Textgrid.save()
218            but it has no impact on a Klattgrid
219        """
220
221        # Header
222        outputTxt = ""
223        outputTxt += 'File type = "ooTextFile"\n'
224        outputTxt += 'Object class = "KlattGrid"\n\n'
225        self.minTimestamp = toIntOrFloat(self.minTimestamp)
226        outputTxt += "xmin = %s\nxmax = %s\n" % (
227            repr(self.minTimestamp),
228            repr(self.maxTimestamp),
229        )
230
231        for tierName in self.tierNames:
232            outputTxt += self._tierDict[tierName].getAsText()
233
234        outputTxt = _cleanNumericValues(outputTxt)
235
236        with io.open(fn, "w", encoding="utf-8") as fd:
237            fd.write(outputTxt)

A container that stores and operates over interval and point tiers

Textgrids are used by the Praat software to group tiers. Each tier contains different annotation information for an audio recording.

Attributes:
  • tierNames(Tuple[str]): the list of tier names in the textgrid
  • tiers(Tuple[TextgridTier]): the list of ordered tiers
  • minTimestamp(float): the minimum allowable timestamp in the textgrid
  • maxTimestamp(float): the maximum allowable timestamp in the textgrid
def save(self, fn: str, minimumIntervalLength: Optional[float] = None):
214    def save(self, fn: str, minimumIntervalLength: Optional[float] = None):
215        """
216
217        minimumIntervalLength is used for compatibility with Textgrid.save()
218            but it has no impact on a Klattgrid
219        """
220
221        # Header
222        outputTxt = ""
223        outputTxt += 'File type = "ooTextFile"\n'
224        outputTxt += 'Object class = "KlattGrid"\n\n'
225        self.minTimestamp = toIntOrFloat(self.minTimestamp)
226        outputTxt += "xmin = %s\nxmax = %s\n" % (
227            repr(self.minTimestamp),
228            repr(self.maxTimestamp),
229        )
230
231        for tierName in self.tierNames:
232            outputTxt += self._tierDict[tierName].getAsText()
233
234        outputTxt = _cleanNumericValues(outputTxt)
235
236        with io.open(fn, "w", encoding="utf-8") as fd:
237            fd.write(outputTxt)

minimumIntervalLength is used for compatibility with Textgrid.save() but it has no impact on a Klattgrid

def toIntOrFloat(val: Union[str, float]) -> float:
240def toIntOrFloat(val: Union[str, float]) -> float:
241    if float(val) - float(int(val)) == 0.0:
242        val = int(val)
243    else:
244        val = float(val)
245    return val