Code refactoring
[sgc2.git] / MainPage.praat
blob51dc5bc1fa09c9edf74c044c281dcb9713ef2b8e
2 # SpeakGoodChinese 2.0
3
4 # Praat script handling buttons page
6 #     SpeakGoodChinese: MainPage.praat loads the code needed for the 
7 #     main, practice, page of SGC2 and the sound handling and recognition.
8 #     
9 #     Copyright (C) 2007-2010  R.J.J.H. van Son and 2010 the Netherlands Cancer Institute
10 #     The SpeakGoodChinese team are:
11 #     Guangqin Chen, Zhonyan Chen, Stefan de Koning, Eveline van Hagen, 
12 #     Rob van Son, Dennis Vierkant, David Weenink
13
14 #     This program is free software; you can redistribute it and/or modify
15 #     it under the terms of the GNU General Public License as published by
16 #     the Free Software Foundation; either version 2 of the License, or
17 #     (at your option) any later version.
18
19 #     This program is distributed in the hope that it will be useful,
20 #     but WITHOUT ANY WARRANTY; without even the implied warranty of
21 #     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 #     GNU General Public License for more details.
23
24 #     You should have received a copy of the GNU General Public License
25 #     along with this program; if not, write to the Free Software
26 #     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
27
28 # Includes at the bottom
30 ###############################################################
32 # Button Drawing Routines
34 ###############################################################
36 procedure DrawPrevious .color$ .x .y .size
37         demo '.color$'
38         .size *= 2/3
39         call drawTriangle -1 .x .y .size
40         .currentX = drawTriangle.currentX
41         .endX = .currentX - 0.5
42         .lowY = .y - .size
43         .highY = .y + .size
44         demo Paint rectangle... '.color$' '.currentX' '.endX' '.lowY' '.highY'
45 endproc
47 procedure DrawNext .color$ .x .y .size
48         demo '.color$'
49         .size *= 2/3
50         call drawTriangle 1 .x .y .size
51         .currentX = drawTriangle.currentX
52         .endX = .currentX + 0.5
53         .lowY = .y - .size
54         .highY = .y + .size
55         demo Paint rectangle... '.color$' '.currentX' '.endX' '.lowY' '.highY'
56 endproc
58 procedure DrawWordListUp .color$ .x .y .size
59     .xleft = .x - .size
60     .xright = .x + .size
61     .xmidleft = .x + 0.1
62     .xmidright = .x - 0.1
63     .ylow = .y
64     .yhigh = .y + .size
65         demo '.color$'
66         demo Line width... 3
67         demo Draw line... .xleft .ylow .xmidleft .yhigh
68         demo Draw line... .xright .ylow .xmidright .yhigh
69         demo Line width... 'defaultLineWidth'
70         demo Black
71 endproc
73 procedure DrawWordListDown .color$ .x .y .size
74     .xleft = .x - .size
75     .xright = .x + .size
76     .xmidleft = .x + 0.1
77     .xmidright = .x - 0.1
78     .yhigh = .y + .size
79     .ylow = .y
80         demo '.color$'
81         demo Line width... 3
82         demo Draw line... .xleft .yhigh .xmidleft .ylow
83         demo Draw line... .xright .yhigh .xmidright .ylow
84         demo Line width... 'defaultLineWidth'
85         demo Black
86 endproc
88 procedure drawTriangle .direction .x .y .size
89         # Make sure direction = +/- 1
90         if .direction = 0
91                 .direction = 1 
92         endif
93         .direction /= abs(.direction)
94         
95         .offset = 0.01
96         .currentHeight = .size
97         .currentX = .x - .direction*.size
98         
99         demo Line width... 2.0
101         while .currentHeight> 0
102                 .ystart = .y + .currentHeight
103                 .yend = .y - .currentHeight
104                 demo Draw line... .currentX .ystart .currentX .yend
105                 .currentHeight -= .offset *3/4
106                 .currentX += .direction*.offset * 1.5
107         endwhile
108         demo Line width... 'defaultLineWidth'
109 endproc
111 ###############################################################
113 # Obligatory button Drawing Routines
115 # These MUST be defined
117 ###############################################################
119 procedure DrawRecord .color$ .x .y .size
120         .size /= 2
121     demo Paint circle... '.color$' '.x' '.y' '.size'
122 endproc
124 procedure DrawPlay .color$ .x .y .size
125         demo '.color$'
126         call drawTriangle 1 .x .y .size
127 endproc
129 procedure DrawQuit .color$ .x .y .size
130         demo Colour... '.color$'
131         .lineWidth = 0.5*.size**2
132         demo Line width... '.lineWidth'
133         .xstart = .x - .size
134         .ystart = .y + .size
135         .xend = .x + .size
136         .yend = .y - .size
137         demo Draw line... .xstart .ystart .xend .yend
138         .xstart = .x - .size
139         .ystart = .y - .size
140         .xend = .x + .size
141         .yend = .y + .size
142         demo Draw line... .xstart .ystart .xend .yend
143         demo Line width... 'defaultLineWidth'
144         demo Colour... Black
145 endproc
147 procedure DrawConfig .color$ .x .y .size
148         .size *= 1
149         .lineWidth = 0.4*.size
150         demo Arrow size... '.lineWidth'
151         .lineWidth = 0.4*.size**2
152         demo Line width... '.lineWidth'
153         .y += .size/2
154         .xstart = .x - .size
155         .xend = .x + .size
156         demo Draw arrow... .xstart .y .xend .y
157         demo Line width... 'defaultLineWidth'
158 endproc
160 procedure DrawRefresh .color$ .x .y .size
161         .lineWidth = 0.5*.size**2
162         .size /= 2
163         demo Line width... '.lineWidth'
164         demo Draw arc... '.x' '.y' '.size' 0 270
165         demo Line width... 'defaultLineWidth'
166 endproc
168 ###############################################################
170 # Button Processing Routines
172 ###############################################################
174 procedure processMainPageExample .clickX .clickY .pressed$
175         call generate_example
176 endproc
178 procedure processMainPagePrevious .clickX .clickY .pressed$
179         call clean_up_sound
180         call display_text Grey
181         call previous_word
182         call init_window
183         # Draw the contour
184         call wipeArea 'wipeFeedbackArea$'
185         call display_text Black
186 endproc
188 procedure processMainPageNext .clickX .clickY .pressed$
189         call clean_up_sound
190         call display_text Grey
191         call next_word
192         call init_window
193         # Draw the contour
194         call wipeArea 'wipeFeedbackArea$'
195         call display_text Black
196 endproc
198 procedure processMainPageWordlistUp .clickX .clickY .pressed$
199         call wipeArea 'wipeFeedbackArea$'
200     call load_word_list "'localWordlistDir$'" -1
201         call init_window
202         call display_text Black
203 endproc
205 procedure processMainPageWordlistDown .clickX .clickY .pressed$
206         call wipeArea 'wipeFeedbackArea$'
207     call load_word_list "'localWordlistDir$'" 1
208         call init_window
209         call display_text Black
210 endproc
212 procedure processMainPageGRADE .clickX .clickY .pressed$
213         call setGrade '.pressed$'
214         # Redraw window
215         call init_window
216 endproc
218 procedure processMainPagePinYinArea .clickX .clickY .pressed$
219         # Redraw window
220         sgc.writeAll = 1
221         call display_text Red
222         sgc.writeAll = 0
223 endproc
225 ###############################################################
227 # Obligatory button Processing Routines
229 # These MUST be defined
231 ###############################################################
233 procedure processMainPageQuit .clickX .clickY .pressed$
234         call end_program
235 endproc
237 procedure processMainPageRefresh .clickX .clickY .pressed$
238         call init_window
239 endproc
241 procedure processMainPageConfig .clickX .clickY .pressed$
242         call config_page
243 endproc
245 procedure processMainPageHelp .clickX .clickY .pressed$
246         call help_loop 'buttons$' init_window
247 endproc
249 procedure processMainPagePlay .clickX .clickY .pressed$
250         .table$ = "MainPage"
251         .label$ = "Play"
252         if recordedSound$ <> ""
253                 call play_sound 'sgc.recordedSound'
254                 mainPage.play = 0
255         endif
256         te.buttonPressValue = mainPage.play
257 endproc
259 procedure Set_Play_Button
260         mainPage.play = -1
261         if recordedSound$ <> ""
262                 mainPage.play = 0
263     endif       
264         call Draw_button MainPage Play 'mainPage.play'
265 endproc
267 procedure processMainPageRecord .clickX .clickY .pressed$
268         call count_syllables
269         .recordingTime = recordingTime
270         if count_syllables.number > 2
271                 .recordingTime = recordingTime + 1.0*ceiling((count_syllables.number - 2)/2)
272         endif
273     call record_sound '.recordingTime'
274     call recognizeTone
275         mainPage.play = 0
276         call Set_Play_Button
278     # Wipe screen
279     call wipeArea 'wipeContourArea$'
280    
281     call draw_tone_contour
282     # Write text (again)
283     call display_word_list_name
284     call display_text Black
285         call add_feedback_to_toneevaluation Feedback
286     call write_feedback Feedback
287         select Table Feedback
288         Remove
289 endproc
292 ###############################################################
294 # Miscelaneous supporting code
296 ###############################################################
298 # The example
299 procedure generate_example
300         select Table 'wordlist$'
301         if te.currentWord > 0 and te.currentWord <= te.numberOfWords
302                 select Table 'wordlist$'
303                 .sound$ = Get value... 'te.currentWord' Sound
304                 sgc.pinyin$ = Get value... 'te.currentWord' Pinyin
305                 if .sound$ = "-" or .sound$ = ""
306                         .sound$ = sgc.pinyin$+".wav"
307                 endif
308                 .soundFilePath$ = localWordlistDir$+"/"+wordlistName$+"/"+.sound$
309                 .wordlistDirectory$ = ""
310                 if localWordlistDir$ <> "" and fileReadable("'localWordlistDir$'/'wordlistName$'")
311                         .wordlistDirectory$ = "'localWordlistDir$'/'wordlistName$'"
312                 elsif sgc2wordlists$ <> "" and fileReadable("'sgc2wordlists$'/'wordlistName$'")
313                         .wordlistDirectory$ = "'sgc2wordlists$'/'wordlistName$'"
314                 elsif globalwordlists$ <> "" and fileReadable("'globalwordlists$'/'wordlistName$'")
315                         .wordlistDirectory$ = "'globalwordlists$'/'wordlistName$'"
316                 endif
317                 if .wordlistDirectory$ <> ""
318                         .audioExampleList = Create Strings as file list... AudioList '.wordlistDirectory$'/'sgc.pinyin$'.*
319                         .number_of_examples = Get number of strings
320                         if .number_of_examples > 0
321                                 Randomize
322                                 .sound$ = Get string... 1
323                                 .soundFilePath$ = "'.wordlistDirectory$'/'.sound$'"
324                         endif
325                         Remove
326                 endif
327                 if fileReadable(.soundFilePath$) and config.useSoundExample
328                         .tmp = -1
329                         .tmp = nocheck Read from file... '.soundFilePath$'
330                         if .tmp != undefined and .tmp > 0
331                                 call play_sound '.tmp'
332                                 select .tmp
333                                 Remove
334                         endif
335                 elsif config.synthesis$ <> "" and config.synthesis$ <> "_DISABLED_"
336                         call synthesize_sound 'sgc.pinyin$'
337                 else
338                         call humToneContour 'sgc.pinyin$' 'config.register'
339                         call reset_viewport
340                 endif
341         endif
342         demoShow()
343 endproc
345 # Draw a tone contour
346 procedure draw_tone_contour
347         select Table 'wordlist$'
348         if te.currentWord > 0 and te.currentWord <= te.numberOfWords
349                 .sound$ = Get value... 'te.currentWord' Sound
350                 sgc.pinyin$ = Get value... 'te.currentWord' Pinyin
351                 call drawToneContour 'sgc.pinyin$' 'config.register'
352                 call reset_viewport
353                 
354                 if te.recordedPitch > 0
355                         call drawSourceToneContour te.recordedPitch
356                 endif
357         endif
359 endproc
361 procedure recognizeTone
362         select Table 'wordlist$'
363         if te.currentWord > 0 and te.currentWord <= te.numberOfWords
364                 .strict = 0
365                 if index_regex(config.strict$, "[^0-9]") <= 0
366                         .strict = ('config.strict$' >= sgc.highestLevel)
367                 endif
368                         
369                 .sound$ = Get value... 'te.currentWord' Sound
370                 sgc.pinyin$ = Get value... 'te.currentWord' Pinyin
371                 call align_recordedSound 'sgc.pinyin$'
372         call sgc_ToneProt 'recordedSound$' 'sgc.pinyin$' 'config.register' '.strict' 'config.language$'
373         # sgc_ToneProt manipulates the sound given. Reconnect
374         select Sound 'recordedSound$'
375         sgc.recordedSound = selected("Sound")
376                 call reset_viewport
377         endif
378 endproc
380 procedure write_feedback .table$
381     select Table '.table$'
382     .line1$ = Get value... 1 Text
383     .line2$ = Get value... 2 Text
384     .label$ = Get value... 3 Text
386         # convert numbers to Pinyin if needed
387         if not config.displayNumbers
388                 call numbers2pinyin '.line1$'
389                 .line1$ = numbers2pinyin.pinyin$
390         endif
392     .color$ = "Red"
393     if index(.line1$, "???") > 0
394         .color$ = "Black"
395     elsif .label$ = "Correct"
396         .color$ = "Green"
397         elsif config.strict$ = "'sgc.highestLevel'"
398                 .line2$ = .line2$ + " *"
399     endif
401         .currentFeedbackFontSize = 14
402         .maxHeight = 21 - 17
403         call adjustFontSizeOnHeight 'defaultFont$' '.currentFeedbackFontSize' '.maxHeight'
404         .currentFeedbackFontSize = adjustFontSizeOnHeight.newFontSize
405         
406         call wipeArea 'wipeFeedbackArea$'
407     call set_font_size '.currentFeedbackFontSize'
408     demo '.color$'
409     demo Text... 50 Centre 21 Bottom '.line1$'
410     demo Text... 50 Centre 17 Bottom '.line2$'
411         demoShow()
412         demo 'defaultFont$'
413     call set_font_size 'defaultFontSize'
414 endproc
416 # Text display
417 procedure display_text .color$
418         select Table 'wordlist$'
419         if te.currentWord < 0 or te.currentWord > te.numberOfWords+1
420                 call clean_up_sound
421         if config.shuffleLists
422                     Randomize rows
423         endif
424                 if te.currentWord < 0
425                         te.currentWord = te.numberOfWords
426                 else
427                         te.currentWord = 1
428                 endif
429         endif
430         
431         if te.currentWord > 0 and te.currentWord <= te.numberOfWords
432                 .displayText$ = ""
433                 .displayPinyin$ = Get value... 'te.currentWord' Pinyin
434                 .displayChar$ = Get value... 'te.currentWord' Character
435                 .displayTrans$ = Get value... 'te.currentWord' Translation
436                 if .displayPinyin$ <> "-" and (config.displayPinyin or sgc.writeAll)
437                         if not config.displayNumbers
438                                 call numbers2pinyin '.displayPinyin$'
439                                 .displayPinyin$ = numbers2pinyin.pinyin$
440                         endif
441                         # Insert u umlaut
442                         .displayPinyin$ = replace_regex$(.displayPinyin$, "v", "\\u\X22", 0)
443                         .displayText$ = .displayText$ + .displayPinyin$
444                 endif
445                 if .displayChar$ <> "-" and (config.displayChar or sgc.writeAll)
446                         .displayText$ = .displayText$ + "  "+ .displayChar$
447                 endif
448                 if .displayTrans$ <> "-" and (config.displayTrans or sgc.writeAll)
449                         .displayText$ = .displayText$ + "  \s{%%"+ .displayTrans$ + "%}"
450                 endif
451         elsif te.currentWord = 0 or te.currentWord = te.numberOfWords+1
452                 call clean_up_sound
453                 .displayText$ = "---"
454         endif
456         # Adapt font size
457         call adjustFontSizeOnHeight 'defaultFont$' 24 15
458         .currentFontSize = adjustFontSizeOnHeight.newFontSize
459         call adjustFontSizeOnWidth 'defaultFont$' '.currentFontSize' 95 '.displayText$'
460         .currentFontSize = adjustFontSizeOnWidth.newFontSize
462         # Clear the writing area
463         call wipeArea 'wipePinyinArea$'
464         # Actually display text
465         demo '.color$'
466         call set_font_size '.currentFontSize'
467         demo Text... 50 Centre 26 Bottom '.displayText$'
468         demoShow()
469         demo Black
470         demo 'defaultFont$'
471         call set_font_size 'defaultFontSize'
472 endproc
474 procedure numbers2pinyin .numberstext$
475         .intermediatePinyin$ = .numberstext$
476         # Move numbers to the nucleus vowel
477         # Tot he vowel
478         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([aeuiov])([^aeuiov0-9]*)([0-9])", "\1\3\2", 0)
479         # Either a/e
480         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([ae])([aeuiov]*)([0-9])", "\1\3\2", 0)
481         # Or the Oo in /ou/
482         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "(ou)([0-9])", "o\2u", 0)
483         # or the second vowel
484         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([uiov][aeuiov])([uiov])([0-9])", "\1\3\2", 0)
485         
486         # Convert all to special characters
487         # Tone 1
488         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "a1", "ā", 0)
489         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "e1", "ē", 0)
490         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "u1", "ū", 0)
491         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "i1", "ī", 0)
492         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "o1", "ō", 0)
493         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v1", "ǖ", 0)
494         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "1", "\\-^", 0)
495         
496         # Tone 2
497         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v2", "ǘ", 0)
498         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([iaeou])2", "\\\1'", 0)
499         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "2", "\\'^", 0)
500         
501         # Tone 3
502         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "a3", "ǎ", 0)
503         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "e3", "ě", 0)
504         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "u3", "ǔ", 0)
505         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "i3", "ǐ", 0)
506         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "o3", "ǒ", 0)
507         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v3", "ǚ", 0)
508         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "3", "\\N^", 0)
510         # Tone 4
511         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v4", "ǜ", 0)
512         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([iaeou])4", "\\\1`", 0)
513         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "4", "\\`^", 0)
514         
515         # Tone 0
516         # Remove tone 0 symbol completely
517         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "0", "", 0)
518         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "a0", "å", 0)
519         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "e0", "e̊", 0)
520         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "u0", "ů", 0)
521         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "i0", "i̊", 0)
522         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "o0", "o̊", 0)
523         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v0", "ü̊", 0)
524         #.intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "0", "\\0^", 0)
525         
526         # Pick best vowel symbols available in cases not caught before
527         # Ugly clutch to get the 1, 3, 0 tone diacritics at least in the neighbourhood
528         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "i(\\[-N0]\^)", "i\\s{_ }\1", 0)
529         # Insert u umlaut
530         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "v", "\\u\X22", 0)
532         .pinyin$ = .intermediatePinyin$
533 endproc
535 # NEEDS WORK AND TESTING!!
536 # Convert unicode Pinyin into tone numbers
537 procedure pinyin2numbers .pinyin$
538         .intermediatePinyin$ = .pinyin$
539         # Convert all special characters to numbers
540         # Tone 1
541         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ā([iaeouü]*)", "a\11", 0)
542         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ē([iaeouü]*)", "e\11", 0)
543         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ū([iaeouü]*)", "u\11", 0)
544         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ī([iaeouü]*)", "i\11", 0)
545         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ō([iaeouü]*)", "o\11", 0)
546         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǖ([iaeouü]*)", "v\11", 0)
547         
548         # Tone 2
549         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "á([iaeouü]*)", "a\12", 0)
550         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "é([iaeouü]*)", "e\12", 0)
551         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ú([iaeouü]*)", "u\12", 0)
552         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "í([iaeouü]*)", "i\12", 0)
553         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ó([iaeouü]*)", "o\12", 0)
554         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǘ([iaeouü]*)", "v\12", 0)
555         
556         # Tone 3
557         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǎ([iaeouü]*)", "a\13", 0)
558         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ě([iaeouü]*)", "e\13", 0)
559         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǔ([iaeouü]*)", "u\13", 0)
560         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǐ([iaeouü]*)", "i\13", 0)
561         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǒ([iaeouü]*)", "o\13", 0)
562         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǚ([iaeouü]*)", "v\13", 0)
564         # Tone 4
565         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "à([iaeouü]*)", "a\14", 0)
566         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "è([iaeouü]*)", "e\14", 0)
567         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ù([iaeouü]*)", "u\14", 0)
568         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ì([iaeouü]*)", "i\14", 0)
569         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ò([iaeouü]*)", "o\14", 0)
570         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ǜ([iaeouü]*)", "v\14", 0)
571         
572         # Tone 0
573         # Add tone 0
574         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "å([iaeouü]*)", "a\10", 0)
575         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "e̊([iaeouü]*)", "e\10", 0)
576         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ů([iaeouü]*)", "u\10", 0)
577         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "i̊([iaeouü]*)", "i\10", 0)
578         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "o̊([iaeouü]*)", "o\10", 0)
579         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "ü̊([iaeouü]*)", "v\10", 0)
581         # Syllables without a tone symbol are tone 0
582         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([aeuiov]+)([^0-9aeuiov]|$)", "\10\2", 0)
584         # Move numbers to the end of the syllable
585         .intermediatePinyin$ = replace_regex$(.intermediatePinyin$, "([aeuiov])([0-9])((ng?)([^aeuiov]|$))?", "\1\3\2\4", 0)
586         
587         .numberstext$ = .intermediatePinyin$
588 endproc
590 # Includes
591 include ToneProt/SGC_ToneProt.praat
592 include ToneProt/DrawToneContour.praat
593 include ToneProt/HumToneContour.praat
594 include ToneProt/ToneRecognition.praat
595 include ToneProt/ToneScript.praat
596 include ToneProt/ToneRules.praat