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

Contains a set of intermediate tiers

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

Modify values in every tier contained in the named intermediate tier

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

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

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

A container that stores and operates over interval and point tiers

def crop(self):
141    def crop(self):
142        raise NotImplementedError
def dejitter(self):
144    def dejitter(self):
145        raise NotImplementedError
def deleteEntry(self, entry):
147    def deleteEntry(self, entry):
148        raise NotImplementedError
def editTimestamps(self):
150    def editTimestamps(self):
151        raise NotImplementedError
def eraseRegion(self):
153    def eraseRegion(self):
154        raise NotImplementedError
def insertEntry(self):
156    def insertEntry(self):
157        raise NotImplementedError
def insertSpace(self):
159    def insertSpace(self):
160        raise NotImplementedError
timestamps
def validate(self):
166    def validate(self):
167        raise NotImplementedError
def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
169    def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
170        newEntries = [
171            (timestamp, modFunc(float(value))) for timestamp, value in self.entries
172        ]
173
174        self._entries = newEntries
def getAsText(self) -> str:
176    def getAsText(self) -> str:
177        outputList = []
178        self.minTimestamp = toIntOrFloat(self.minTimestamp)
179        outputList.append("%s? <exists> " % self.name)
180        outputList.append("xmin = %s" % my_math.numToStr(self.minTimestamp))
181        outputList.append("xmax = %s" % my_math.numToStr(self.maxTimestamp))
182
183        if self.name not in ["phonation", "vocalTract", "coupling", "frication"]:
184            outputList.append("points: size= %d" % len(self.entries))
185
186        for i, entry in enumerate(self.entries):
187            outputList.append("points [%d]:" % (i + 1))
188            outputList.append("    number = %s" % my_math.numToStr(entry[0]))
189            outputList.append("    value = %s" % my_math.numToStr(entry[1]))
190
191        return "\n".join(outputList) + "\n"
class KlattSubPointTier(KlattPointTier):
194class KlattSubPointTier(KlattPointTier):
195    """
196    Tiers contained in a KlattIntermediateTier
197    """
198
199    def getAsText(self) -> str:
200        outputList = []
201        outputList.append("%s:" % self.name)
202        self.minTimestamp = toIntOrFloat(self.minTimestamp)
203        outputList.append("    xmin = %s" % my_math.numToStr(self.minTimestamp))
204        outputList.append("    xmax = %s" % my_math.numToStr(self.maxTimestamp))
205        outputList.append("    points: size = %d" % len(self.entries))
206
207        for i, entry in enumerate(self.entries):
208            outputList.append("    points [%d]:" % (i + 1))
209            outputList.append("        number = %s" % my_math.numToStr(entry[0]))
210            outputList.append("        value = %s" % my_math.numToStr(entry[1]))
211
212        return "\n".join(outputList) + "\n"

Tiers contained in a KlattIntermediateTier

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