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
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
Inherited Members
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)
Inherited Members
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
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"
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"
Inherited Members
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: