3 # Generate a Pitch object from the pinyin
5 # For testing the models.
7 form Enter pinyin and tone 1 frequency
8 word inputWord xiang1gang3
9 positive upperRegister_(Hz) 169
10 real range_Factor 0.8408964152537146
11 real durationScale 0.6511624373537163
19 # To supress the ToneList, change to 0
21 if rindex_regex(generate$, "Correct") > 0
30 inputWord$ = replace_regex$(inputWord$, "^\s*(.+)\s*$", "\1", 1)
33 procedure extractTone syllable$
35 currentToneText$ = replace_regex$(syllable$, "^[^\d]+([\d]+)(.*)$", "\1", 0)
36 toneSyllable = extractNumber(currentToneText$, "")
39 procedure convertVoicing voicingSyllable$
41 voicingSyllable$ = replace_regex$(voicingSyllable$, "^([^\d]+)[\d]+", "\1", 0)
42 # Convert voiced consonants
43 voicingSyllable$ = replace_regex$(voicingSyllable$, "(ng|[wrlmny])", "C", 0)
44 # Convert unvoiced consonants
45 voicingSyllable$ = replace_regex$(voicingSyllable$, "(sh|ch|zh|[fsxhktpgqdbzcj])", "U", 0)
47 voicingSyllable$ = replace_regex$(voicingSyllable$, "([aiuoeĆ¼])", "V", 0)
50 # Add a tone movement. The current time point is 'point'
55 segmentDuration = 0.150
62 nineSemit = 0.594603557501361
64 sixSemit = 0.707106781186547
65 # 1/(3 semitones) down
66 threeSemit = 0.840896415253715
67 # 1/(2 semitones) down
68 twoSemit = 0.890898718140339
69 # 1/(1 semitones) down
70 oneSemit = 0.943874313
72 fiveSemit = threeSemit * twoSemit
74 frequency_Range = octave
76 frequency_Range = frequency_Range * range_Factor
79 # Previous end frequency
81 procedure addToneMovement syllable$ topLine prevTone nextTone
84 call extractTone 'syllable$'
85 if toneSyllable = 3 and nextTone = 3
87 elsif syllable$ = "bu4" and nextTone = 4
93 call convertVoicing 'syllable$'
95 # Account for tones in duration
99 elsif toneSyllable = 2
101 elsif toneSyllable = 4
104 toneFactor = toneFactor * durationScale
107 if rindex_regex(voicingSyllable$, "U") = 1
108 point = point + delta
109 Add point... 'point' 0
110 point = point + segmentDuration * toneFactor
111 Add point... 'point' 0
114 voiceLength$ = replace_regex$(voicingSyllable$, "U*([CV]+)U*", "\1", 0)
115 voicedLength = length(voiceLength$)
116 voicedDuration = toneFactor * (segmentDuration*voicedLength + fixedDuration)
117 point = point + delta
119 # Write contour of each tone
120 # Note that tones are influenced by the previous (tone 0) and next (tone 3)
121 # tones. Tone 6 is the Dutch intonation
122 # sqrt(frequency_Range) is the mid point
123 if topLine * frequency_Range < absoluteMinimum
124 frequency_Range = absoluteMinimum / topLine
128 Add point... 'point' 'topLine'
129 point = point + voicedDuration
130 Add point... 'point' 'topLine'
131 lastFrequency = topLine
132 elsif toneSyllable = 2
133 # Halfway of the range
134 startPoint = topLine * sqrt(frequency_Range)
136 # Special case: 2 followed by 1, stop short of the top-line
138 endPoint = startPoint / threeSemit
140 # Go lower if previous tone is 1
142 startPoint = startPoint * oneSemit
144 # Special case: 2 following 4, stop short of top-line
145 endPoint = startPoint / threeSemit
148 Add point... 'point' 'startPoint'
149 point = point + voicedDuration
150 Add point... 'point' 'endPoint'
151 lastFrequency = topLine
152 elsif toneSyllable = 3
154 startPoint = topLine * sqrt(frequency_Range)
155 lowestPoint = topLine * frequency_Range * threeSemit
156 # Protect pitch against "underflow"
157 if lowestPoint < absoluteMinimum
158 lowestPoint = absoluteMinimum
162 endPoint = startPoint
163 elsif nextTone = 1 or nextTone = 4
164 lowestPoint = topLine * frequency_Range / twoSemit
165 endPoint = startPoint
167 endPoint = topLine * frequency_Range / twoSemit
169 endPoint = startPoint
172 Add point... 'point' 'startPoint'
173 point = point + (voicedDuration)/3
174 Add point... 'point' 'lowestPoint'
175 point = point + (voicedDuration)/3
176 Add point... 'point' 'lowestPoint'
177 point = point + (voicedDuration)/3
178 Add point... 'point' 'endPoint'
179 lastFrequency = topLine
180 elsif toneSyllable = 4
181 startPoint = topLine / twoSemit
182 endPoint = startPoint * frequency_Range
183 Add point... 'point' 'startPoint'
184 point = point + voicedDuration/3
185 Add point... 'point' 'startPoint'
186 point = point + voicedDuration*2/3
187 Add point... 'point' 'endPoint'
188 lastFrequency = endPoint
189 elsif toneSyllable = 0
190 if prevTone = 1 or prevTone = 2
191 zeroPoint = topLine * sixSemit * oneSemit
193 zeroPoint = topLine * threeSemit
195 zeroPoint = topLine * frequency_Range / threeSemit
197 zeroPoint = topLine * sixSemit * oneSemit
200 Add point... 'point' 'zeroPoint'
201 point = point + voicedDuration
202 Add point... 'point' 'zeroPoint'
203 lastFrequency = zeroPoint
205 dutchInton = topLine * sqrt(frequency_Range)
207 dutchInton = lastFrequency
210 endPoint = dutchInton * oneSemit
211 Add point... 'point' 'dutchInton'
212 point = point + voicedDuration
213 Add point... 'point' 'endPoint'
214 lastFrequency = endPoint
219 # Split input into syllables
222 procedure wordToTones wordInput$ highPitch
223 currentRest$ = wordInput$;
228 while rindex_regex(currentRest$, "^[^\d]+[\d]+") > 0
230 syllable'syllableCount'$ = replace_regex$(currentRest$, "^([^\d]+[\d]+)(.*)$", "\1", 1)
231 currentSyllable$ = syllable'syllableCount'$
234 call extractTone 'currentSyllable$'
235 toneSyllable'syllableCount' = toneSyllable
236 currentTone = toneSyllable'syllableCount'
238 # Get the Voicing pattern
239 call convertVoicing 'currentSyllable$'
240 voicingSyllable'syllableCount'$ = voicingSyllable$
241 currentVoicing$ = voicingSyllable'syllableCount'$
243 # Calculate new length
247 elsif currentTone = 4
250 toneFactor = toneFactor * durationScale
252 length = length + toneFactor * (length(voicingSyllable'syllableCount'$) * (segmentDuration + delta) + fixedDuration)
255 currentRest$ = replace_regex$(currentRest$, "^([^\d]+[\d]+)(.*)$", "\2", 1)
258 if syllableCount > 2000
263 # Create tone pattern
264 Create PitchTier... 'wordInput$' 0 'length'
269 Add point... 'point' 0
273 for i from 1 to syllableCount
274 currentSyllable$ = syllable'i'$
275 currentTone = toneSyllable'i'
279 followTone = toneSyllable'j'
282 call addToneMovement 'currentSyllable$' 'highPitch' 'lastTone' 'followTone'
284 lastTone = currentTone
288 point = point + delta
289 Add point... 'point' 0
290 point = point + margin
291 Add point... 'point' 0
294 procedure generateWord whatToGenerate$ theWord$ upperRegister
295 call wordToTones 'theWord$' 'upperRegister'
297 select PitchTier 'theWord$'
298 noprogress To Pitch... 0.0125 60.0 600.0
299 Rename... theOrigWord
302 select Pitch theOrigWord
305 # Generate sound if wanted
306 select Pitch 'theWord$'
307 if rindex_regex(whatToGenerate$, "Sound") > 0
308 noprogress To Sound (hum)
312 select PitchTier 'theWord$'
313 if rindex_regex(whatToGenerate$, "Sound") > 0
314 plus Pitch 'theWord$'
319 # Horrible clutch to get an empty Strings list
320 if createToneList = 1
321 Read Strings from raw text file... ToneScript.praat
323 listLength = Get number of strings
324 for i from 1 to listLength
325 select Strings ToneList
326 Set string... 'i' ------EMPTY
330 syllableCount = length(replace_regex$(inputWord$, "[^\d]+([\d]+)", "1", 0))
332 if rindex(generate$, "Correct") <= 0
333 for first from 1 to 6
334 currentWord$ = replace_regex$(inputWord$, "^([^\d]+)([\d]+)(.*)$", "\1'first'\3", 1)
335 for second from 0 to 6
336 if (first <> 5 and second <> 5) and (syllableCount > 1 or second == 1)
337 currentWord$ = replace_regex$(currentWord$, "^([^\d]+)([\d]+)([^\d]+)([\d]+)$", "\1'first'\3'second'", 1)
339 wordNumber = wordNumber+1
340 if createToneList = 1
341 select Strings ToneList
342 Set string... 'wordNumber' 'currentWord$'
345 # Actually, generate something
346 call generateWord 'generate$' 'currentWord$' 'upperRegister'
351 call generateWord 'generate$' 'inputWord$' 'upperRegister'