praatio.klattgrid
Functions for reading/writing/manipulating klattgrid files
A klattgrid can be used for speech synthesis/resynthesis. For more information on the praat klattgrid: http://www.fon.hum.uva.nl/praat/manual/KlattGrid.html
There are three kinds of data types in a klattgrid: null tiers (contain no data points -- seem to function as headers for a set of regular tiers) regular tiers embedded tiers
In this code: null tiers and regular tiers are both represented by KlattPointTier
embedded tiers contain tiers of tiers (3 layers) A KlattContainerTier contains a list of KlattIntermediateTiers which contains a list of KlattSubPointTiers. Only the KlattSubPointTiers contain any points
see examples/klatt_resynthesis.py
1"""Functions for reading/writing/manipulating klattgrid files 2 3A klattgrid can be used for speech synthesis/resynthesis. 4For more information on the praat klattgrid: 5http://www.fon.hum.uva.nl/praat/manual/KlattGrid.html 6 7There are three kinds of data types in a klattgrid: 8null tiers (contain no data points -- seem to function as headers for a 9 set of regular tiers) 10regular tiers 11embedded tiers 12 13In this code: 14null tiers and regular tiers are both represented by KlattPointTier 15 16embedded tiers contain tiers of tiers (3 layers) 17A KlattContainerTier contains a list of KlattIntermediateTiers which 18contains a list of KlattSubPointTiers. Only the KlattSubPointTiers contain 19any points 20 21see **examples/klatt_resynthesis.py** 22""" 23 24import io 25from os.path import join 26from typing import List, Tuple, Optional 27 28from praatio.data_classes.klattgrid import ( 29 Klattgrid, 30 KlattPointTier, 31 KlattContainerTier, 32 KlattSubPointTier, 33 KlattIntermediateTier, 34) 35from praatio.utilities import utils 36 37 38def openKlattgrid(fnFullPath: str) -> Klattgrid: 39 try: 40 with io.open(fnFullPath, "r", encoding="utf-16") as fd: 41 data = fd.read() 42 except UnicodeError: 43 with io.open(fnFullPath, "r", encoding="utf-8") as fd: 44 data = fd.read() 45 data = data.replace("\r\n", "\n") 46 47 # Right now, can only open normal klatt grid and not short ones 48 kg = _openNormalKlattgrid(data) 49 50 return kg 51 52 53def wavToKlattgrid( 54 praatEXE: str, 55 inputFullPath: str, 56 outputFullPath: str, 57 timeStep: float = 0.005, 58 numFormants: int = 5, 59 maxFormantFreq: float = 5500.0, 60 windowLength: float = 0.025, 61 preEmphasis: float = 50.0, 62 pitchFloor: float = 60.0, 63 pitchCeiling: float = 600.0, 64 minPitch: float = 50.0, 65 subtractMean: bool = True, 66 scriptFN: Optional[str] = None, 67) -> None: 68 """Extracts the klattgrid from a wav file 69 70 The default values are the same as the ones used in praat 71 """ 72 73 if subtractMean is True: 74 subtractMeanStr = "yes" 75 else: 76 subtractMeanStr = "no" 77 78 if scriptFN is None: 79 scriptFN = join(utils.scriptsPath, "sound_to_klattgrid.praat") 80 81 utils.runPraatScript( 82 praatEXE, 83 scriptFN, 84 [ 85 inputFullPath, 86 outputFullPath, 87 timeStep, 88 numFormants, 89 maxFormantFreq, 90 windowLength, 91 preEmphasis, 92 pitchFloor, 93 pitchCeiling, 94 minPitch, 95 subtractMeanStr, 96 ], 97 ) 98 99 100def resynthesize( 101 praatEXE: str, 102 wavFN: str, 103 klattFN: str, 104 outputWavFN: str, 105 doCascade: bool = True, 106 scriptFN: Optional[str] = None, 107) -> None: 108 if doCascade: 109 method = "Cascade" 110 else: 111 method = "Parallel" 112 113 if scriptFN is None: 114 scriptFN = join(utils.scriptsPath, "resynthesize_from_klattgrid.praat") 115 116 # Praat crashes on exit after resynthesis with a klattgrid 117 utils.runPraatScript(praatEXE, scriptFN, [wavFN, klattFN, outputWavFN, method]) 118 119 120def _openNormalKlattgrid(data: str) -> Klattgrid: 121 kg = Klattgrid() 122 123 # Toss header 124 data = data.split("\n\n", 1)[1] 125 126 # Not sure if this is needed 127 startI = data.index("points") 128 startI = data.index("\n", startI) 129 130 # Find sections 131 sectionIndexList = _findIndicies(data, "<exists>") 132 133 sectionIndexList.append(-1) 134 135 for i in range(len(sectionIndexList) - 1): 136 dataTuple = _getSectionHeader(data, sectionIndexList, i) 137 name, minT, maxT, sectionData, sectionTuple = dataTuple 138 139 # Container Tier 140 if name in [ 141 "oral_formants", 142 "nasal_formants", 143 "nasal_antiformants", 144 "tracheal_formants", 145 "tracheal_antiformants", 146 "delta_formants", 147 "frication_formants", 148 ]: 149 kct = _proccessContainerTierInput(sectionData, name) 150 kg.addTier(kct) 151 152 else: 153 # Process entries if this tier has any 154 entries = _buildEntries(sectionTuple) 155 tier = KlattPointTier(name, entries, minT, maxT) 156 kg.addTier(tier) 157 158 return kg 159 160 161def _proccessContainerTierInput(sectionData: str, name: str): 162 sectionData = sectionData.split("\n", 3)[-1] 163 164 formantIndexList = _findIndicies(sectionData, "formants") 165 166 subFilterList = [ 167 "bandwidths", 168 "oral_formants_amplitudes", 169 "nasal_formants_amplitudes", 170 "tracheal_formants_amplitudes", 171 "frication_formants_amplitudes", 172 ] 173 174 # Find the index of all the different data sections 175 subFilterIndexList = [ 176 _findIndicies(sectionData, subName) for subName in subFilterList 177 ] 178 179 # 'Formant' search query finds duplicates -- remove them 180 newFormantList = [] 181 for value in formantIndexList: 182 if all([value not in subList for subList in subFilterIndexList]): 183 newFormantList.append(value) 184 formantIndexList = newFormantList 185 186 # Combine regular query with formant query 187 indexListOfLists = [ 188 formantIndexList, 189 ] + subFilterIndexList 190 191 # Flatten index list 192 masterIndexList = [value for sublist in indexListOfLists for value in sublist] 193 masterIndexList.sort() 194 195 # If an index list is last, it it needs to include '-1' to capture the 196 # rest of the data 197 for subList in indexListOfLists: 198 try: 199 val = subList[-1] 200 except IndexError: 201 continue 202 ii = masterIndexList.index(val) # Index of the index 203 try: 204 subList.append(masterIndexList[ii + 1] - 1) 205 except IndexError: 206 subList.append(-1) 207 208 # Build the tier structure 209 kct = KlattContainerTier(name) 210 for indexList in indexListOfLists: 211 if indexList == []: 212 continue 213 tierList = [] 214 for j in range(len(indexList) - 1): 215 try: 216 tmpTuple = _getSectionHeader(sectionData, indexList, j) 217 except ValueError: 218 continue 219 subName, subMin, subMax, _, subTuple = tmpTuple 220 subName = subName[:-1] 221 222 entries = _buildEntries(subTuple) 223 tier = KlattSubPointTier(subName, entries, subMin, subMax) 224 tierList.append(tier) 225 kit = KlattIntermediateTier(subName.split()[0]) 226 for tier in tierList: 227 kit.addTier(tier) 228 kct.addTier(kit) 229 230 return kct 231 232 233def _findIndicies(data, keyword): 234 indexList = utils.findAll(data, keyword) 235 indexList = [data.rfind("\n", 0, i) for i in indexList] 236 237 return indexList 238 239 240def _getSectionHeader(data, indexList, i): 241 sectionStart = indexList[i] 242 sectionEnd = indexList[i + 1] 243 sectionData = data[sectionStart:sectionEnd].strip() 244 sectionTuple = sectionData.split("\n", 4) 245 246 subheader, minr, maxr = sectionTuple[:3] 247 name = subheader.split("?")[0].strip() 248 minT = float(minr.split("=")[1].strip()) 249 maxT = float(maxr.split("=")[1].strip()) 250 251 tail = sectionTuple[3:] 252 253 return name, minT, maxT, sectionData, tail 254 255 256def _buildEntries(sectionTuple): 257 entries = [] 258 if len(sectionTuple) > 1: # Has points 259 npoints = float(sectionTuple[0].split("=")[1].strip()) 260 if npoints > 0: 261 entries = _processSectionData(sectionTuple[1]) 262 263 return entries 264 265 266def _processSectionData(sectionData: str) -> List[Tuple[float, float]]: 267 sectionData += "\n" 268 269 startI = 0 270 tupleList = [] 271 while True: 272 try: 273 startI = sectionData.index("=", startI) + 1 # Past the equal sign 274 except ValueError: # No more data 275 break 276 277 endI = sectionData.index("\n", startI) 278 time = float(sectionData[startI:endI].strip()) 279 280 startI = sectionData.index("=", endI) + 1 # Just past the '=' sign 281 endI = sectionData.index("\n", startI) 282 value = float(sectionData[startI:endI].strip()) 283 284 startI = endI 285 tupleList.append((time, value)) 286 287 return tupleList
39def openKlattgrid(fnFullPath: str) -> Klattgrid: 40 try: 41 with io.open(fnFullPath, "r", encoding="utf-16") as fd: 42 data = fd.read() 43 except UnicodeError: 44 with io.open(fnFullPath, "r", encoding="utf-8") as fd: 45 data = fd.read() 46 data = data.replace("\r\n", "\n") 47 48 # Right now, can only open normal klatt grid and not short ones 49 kg = _openNormalKlattgrid(data) 50 51 return kg
54def wavToKlattgrid( 55 praatEXE: str, 56 inputFullPath: str, 57 outputFullPath: str, 58 timeStep: float = 0.005, 59 numFormants: int = 5, 60 maxFormantFreq: float = 5500.0, 61 windowLength: float = 0.025, 62 preEmphasis: float = 50.0, 63 pitchFloor: float = 60.0, 64 pitchCeiling: float = 600.0, 65 minPitch: float = 50.0, 66 subtractMean: bool = True, 67 scriptFN: Optional[str] = None, 68) -> None: 69 """Extracts the klattgrid from a wav file 70 71 The default values are the same as the ones used in praat 72 """ 73 74 if subtractMean is True: 75 subtractMeanStr = "yes" 76 else: 77 subtractMeanStr = "no" 78 79 if scriptFN is None: 80 scriptFN = join(utils.scriptsPath, "sound_to_klattgrid.praat") 81 82 utils.runPraatScript( 83 praatEXE, 84 scriptFN, 85 [ 86 inputFullPath, 87 outputFullPath, 88 timeStep, 89 numFormants, 90 maxFormantFreq, 91 windowLength, 92 preEmphasis, 93 pitchFloor, 94 pitchCeiling, 95 minPitch, 96 subtractMeanStr, 97 ], 98 )
Extracts the klattgrid from a wav file
The default values are the same as the ones used in praat
101def resynthesize( 102 praatEXE: str, 103 wavFN: str, 104 klattFN: str, 105 outputWavFN: str, 106 doCascade: bool = True, 107 scriptFN: Optional[str] = None, 108) -> None: 109 if doCascade: 110 method = "Cascade" 111 else: 112 method = "Parallel" 113 114 if scriptFN is None: 115 scriptFN = join(utils.scriptsPath, "resynthesize_from_klattgrid.praat") 116 117 # Praat crashes on exit after resynthesis with a klattgrid 118 utils.runPraatScript(praatEXE, scriptFN, [wavFN, klattFN, outputWavFN, method])