Updated shasums
[sgc2.git] / sgc2.praat
blob9bbb3f834217c4080be0c192ddfc65c492b07c76
2 # SpeakGoodChinese 2.0
3
4 # Master Praat script
6 #     SpeakGoodChinese: sgc2.praat is the master GUI of SpeakGoodChinese
7 #     It is written in Praat script for the Demo window 
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 # The real application name
29 sgc2.demoAppName$ = "SpeakGoodChinese2"
31 # Define variable that might be reset in Initialise*.praat
32 if not variableExists("build_SHA$")
33         build_SHA$ = "-"
34 endif
36 # These are simply "useful" defaults
37 # The local Table directory only exists when running SGC as a script
38 if build_SHA$ = "-"
39         localTableDir$ = "Data"
40 else
41         localTableDir$ = ""
42 endif
43 buttonsTableName$ = "MainPage"
44 configTableName$ = "Config"
45 buttons$ = ""
46 config$ = ""
47 recordedSound$ = ""
48 sgc.recordedSound = 0
49 te.recordedPitch = 0
50 te.buttonPressValue = 0
51 samplingFrequency = 44100
52 recordingTime = 2
54 # Pop-Up window colors
55 sgc2.popUp_bordercolor$ = "{0.5,0.5,1}"
56 sgc2.popUp_backgroundcolor$ = "{0.9,0.9,1}"
58 # If running in a packed script binary
59 if index_regex(preferencesDirectory$, "(?i'sgc2.demoAppName$')$")
60         preferencesAppDir$ = preferencesDirectory$
61 elsif index_regex(preferencesDirectory$, "[pP]raat(\-dir| Prefs)?$")
62         # If running as a Praat script, create a new preferences directory
63         if unix
64                 preferencesAppDir$ = "'preferencesDirectory$'/../.'sgc2.demoAppName$'"
65         else
66                 preferencesAppDir$ = "'preferencesDirectory$'/../'sgc2.demoAppName$'"
67         endif
68 else
69         # It has to go somewhere. Make a subdirectory in the current preferences directory
70         preferencesAppDir$ = "'preferencesDirectory$'/'sgc2.demoAppName$'"
71 endif
74 # Parameters for isolating recorded speech from noise
75 # Should be mostly left alone unless you are using ultra clean
76 # or very noisy recordings
77 noiseThresshold = -30
78 minimumPitch = 60
79 soundMargin = 0.25
80 minimumIntensity = 30
82 # Set up button height
83 buttonbevel = 0
85 # Define canvas
86 viewportMargin = 5
87 defaultFontSize = 12
88 defaultFont$ = "Helvetica"
89 defaultLineWidth = 1
91 # Set up system
92 call reset_viewport
94 # Load supporting scripts
95 # Load tables in script format
96 include CreateTables.praat
97 include CreateWordlists.praat
98 # Set up system and load preferences
99 include InitialiseSGC2.praat
100 # Include the main page buttons and procedures
101 include MainPage.praat
102 # Include the configuration page buttons and procedures
103 include Config.praat
105 # A function:
106 # replaySGC2LogFunction$ = "replaySGC2LogFunction"
107 # procedure replaySGC2LogFunction
108 #       ....
109 #       Test commands
110 # endproc
112 # include sgc2.praat
114 'replaySGC2Log$'
116 # Start instruction loop
117 while demoWaitForInput()
118         .label$ = ""
119         .clickX = -1
120         .clickY = -1
121         .pressed$ = ""
122         if demoClicked()
123                 .clickX = demoX()
124                 .clickY = demoY()
125                 call buttonClicked 'buttons$' '.clickX' '.clickY'
126                 .label$ = buttonClicked.label$
127         elsif demoKeyPressed()
128                 .pressed$ = demoKey$()
129                 call keyPressed 'buttons$' '.pressed$'
130                 .label$ = keyPressed.label$
131         endif
133         # You cannot select a text field
134         if startsWith(.label$, "$")
135                 .label$ = ""
136         endif
137         
138         # Do things
139         if .label$ != ""
140                 te.buttonPressValue = 0
141                 # Push button down
142                 call Draw_button 'buttons$' '.label$' 1
143                 call process_label '.label$' '.clickX' '.clickY' '.pressed$'
144                 # push button up
145                 call Draw_button 'buttons$' '.label$' 'te.buttonPressValue'
146         endif
147 endwhile
149 call end_program
152 ########################################################
154 # Definitions of procedures
156 ########################################################
158 # Do what is asked
159 procedure process_label .label$ .clickX .clickY .pressed$
160         # Prcoess the command
161         if .label$ <> "" and not startsWith(.label$,"!")
162                 .label$ = replace_regex$(.label$, "^[#]", "", 0)
163                 .label$ = replace$(.label$, "_", " ", 0)
164                 
165                 # log activity
166                 'sgc2.logging$' call process'buttons$''.label$' '.clickX' '.clickY' '.pressed$'
167                 
168                 call process'buttons$''.label$' '.clickX' '.clickY' '.pressed$'
169         endif
170 endproc
172 # Intialize buttons
173 procedure init_buttons
174         noerase = 1
175         call Draw_all_buttons 'buttons$'
176         noerase = 0
177 endproc
179 # Draw all buttons
180 noerase = 0
181 procedure Draw_all_buttons .table$
182         .varPrefix$ = replace_regex$(.table$, "^(.)", "\l\1", 0)
183         select Table '.table$'
184         .numRows = Get number of rows
185         
186         for .row to .numRows
187                 .label$ = Get value... '.row' Label
188         if not startsWith(.label$, "!")
189                         .pressed = 0
190                         # Determine the "pressed" state of a button
191                         # A variable with the same name as the button will act as a
192                         # "pressed state"
193                         .variableName$ = .varPrefix$+"."+(replace_regex$(.label$, "^(.)", "\l\1", 0))
194                         # Simple boolean variables
195                         if index(.variableName$, "_") <= 0 and variableExists(.variableName$)
196                                 # True: Pressed
197                                 if '.variableName$' > 0
198                                         .pressed = 2
199                                 # <0: Grayed out
200                                 elsif '.variableName$' < 0
201                                         .pressed = -1
202                                 endif
203                         elsif index(.variableName$, "_") <= 0 and variableExists("'.variableName$'$")
204                                 # Non zero: Pressed
205                                 if '.variableName$'$ <> "" and '.variableName$'$ <> "0"
206                                         .pressed = 2
207                                 endif
208                         # Complex buttons with an variableName+'_'+value structure
209                         # varableName$ -> name of button, e.g., "language"
210                         elsif index(.variableName$, "_")
211                                 .generalVar$ = left$(.variableName$, rindex(.variableName$, "_") - 1)
212                                 .currentVariableName$ = .generalVar$
213                                 # Is it a string?
214                                 if variableExists(.generalVar$+"$")
215                                         .currentVariableName$ = .generalVar$ + "$"
216                                 endif
217                                 # Remove one level of indirection
218                                 if variableExists(.currentVariableName$)
219                                         if index(.currentVariableName$, "$")
220                                                 .currentVariableName$ = '.currentVariableName$'
221                                         else
222                                                 .currentValue = '.currentVariableName$'
223                                                 .currentVariableName$ = "'.currentValue'"
224                                         endif
225                                         # Remove next level of indirection
226                                         .currentContent$ = "'.currentVariableName$'"
227                                         if .currentContent$ = "_DISABLED_"
228                                                 .pressed = -1
229                                         endif
230                                         # Reconstruct label from current values
231                                         .currentLabelValue$ = .generalVar$ + "_" + .currentContent$
232                                         # Set PRESSED from label
233                                         if .variableName$ = .currentLabelValue$
234                                                 .pressed = 2
235                                         endif
236                                 endif
237                         endif
238                         # You did erase everything before you started here? So do not do that again
239                     call Draw_button_internal 0 '.table$' '.label$' '.pressed'
240         endif
241         endfor
242 endproc
244 # Draw a button from a predefined button table
245 # Normally, erase the area around a button
246 procedure Draw_button .table$ .label$ .push
247         call Draw_button_internal 1 '.table$' '.label$' '.push'
248 endproc
250 # Use this function if you want to control erasing
251 procedure Draw_button_internal .erase_button_area .table$ .label$ .push
252         # Do not draw invisible buttons starting with #
253         goto NOBUTTON startsWith(.label$, "#")
255         # Scale rounding of rounded rectangles
256         .wc = 1
257         .mm = demo Horizontal wc to mm... '.wc' 
258     # Allow to overide ! skip directive
259     .forceDraw = 0
260     if startsWith(.label$, "+")
261         .label$ = right$(.label$, length(.label$)-1)
262         .forceDraw = 1
263     endif
265     select Table '.table$'
266     .row = Search column... Label '.label$'
267         if .row < 1
268                 call emergency_table_exit Button Table '.table$' does not have a row with label '.label$'
269         endif
270         
271         # Perspective shift sizes
272         .shiftDown = 0
273         .shiftX = 0
274         .shiftY = 0
275         if buttonbevel <> 0
276                 .shiftDown = 0.1*buttonbevel
277         .shiftX = -0.2*buttonbevel
278         .shiftY = 0.40*buttonbevel
279         endif
280         
281         # Set drawing parameters
282         .topBackGroundColorUp$ = "{0.93,0.93,0.93}"
283         .topLineColorUp$ = "Black"
284         .topLineWidthUp = 1.5
285         .topBackGroundColorDown$ = "{0.89,0.89,0.94}"
286         .topLineColorDown$ = "{0.2,0.2,0.2}"
287         .topLineWidthDown = 2.0
288         .topBackGroundColorDisabled$ = "{0.85,0.85,0.85}"
289         .topLineColorDisabled$ = "{0.70,0.70,0.70}"
290         .topLineWidthDisabled = 1.5
291         .flankBackGroundColorUp$ = "{0.6,0.6,0.6}"
292         .flankLineColorUp$ = "{0.2,0.2,0.2}"
293         .flankLineWidthUp = 1.5
294         .flankBackGroundColorDown$ = "{0.75,0.75,0.75}"
295         .flankLineColorDown$ = .flankLineColorUp$
296         .flankLineWidthDown = 1.5
297         .buttonFontSize = defaultFontSize
298         
299         # Get button values
300     .leftX = Get value... '.row' LeftX
301     .rightX = Get value... '.row' RightX
302     .lowY = Get value... '.row' LowY
303     .highY = Get value... '.row' HighY
304     .buttonText$ = Get value... '.row' Text
305     .buttonColor$ = Get value... '.row' Color
306     .buttonDraw$ = Get value... '.row' Draw
307     .buttonKey$ = Get value... '.row' Key
308     
309     .noDraw = startsWith(.label$, "!") or (.leftX < 0) or (.rightX < 0) or (.lowY < 0) or (.highY < 0)
311         .rotation = 0
312         if index_regex(.buttonText$, "^![0-9\.]+!")
313                 .rotation = extractNumber(.buttonText$, "!")
314                 .buttonText$ = replace_regex$(.buttonText$, "^![0-9\.]+!", "", 0)
315         endif
316         
317         if .leftX = .rightX or .highY = .lowY
318                 .noDraw = 1
319         endif
320     
321     goto NOBUTTON .noDraw and not .forceDraw
323     # Replace button text with ALERT
324     if .push = 3
325         .buttonText$ = alertText$
326     endif
327         
328         # Adapt font size to button size
329         .maxWidth = (.rightX - .leftX) - 2
330         .maxHeight = (.highY - .lowY) - 1
331         if .rotation = 0
332                 # Adapt size of button to length of text if necessary
333                 call adjustFontSizeOnWidth 'defaultFont$' '.buttonFontSize' '.maxWidth' '.buttonText$'
334                 .buttonFontSize = adjustFontSizeOnWidth.newFontSize
335                 if adjustFontSizeOnWidth.diff > 0
336                         .rightX += adjustFontSizeOnWidth.diff/2
337                         .leftX -= adjustFontSizeOnWidth.diff/2
338                 endif
339                 call set_font_size '.buttonFontSize'
341                 # Adapt size of button to length of text
342                 call adjustFontSizeOnHeight 'defaultFont$' '.buttonFontSize' '.maxHeight'
343                 if adjustFontSizeOnHeight.diff > 0
344                         .lowY -= adjustFontSizeOnHeight.diff/2
345                         .highY += adjustFontSizeOnHeight.diff/2
346                 endif
347                 .buttonFontSize = adjustFontSizeOnHeight.newFontSize
348         else
349                 # With non-horizontal text, only change font size
350                 call adjustRotatedFontSizeOnBox 'defaultFont$' '.buttonFontSize' '.maxWidth' '.maxHeight' '.rotation' '.buttonText$'
351                 .buttonFontSize = adjustRotatedFontSizeOnBox.newFontSize
352         endif
353         
354         # Reset and erase button area
355         call reset_viewport
356     demo Line width... 'defaultLineWidth'
357     .shiftLeftX = .leftX
358     .shiftRightX = .rightX - .shiftX
359     .shiftLowY = .lowY - .shiftY
360     .shiftHighY = .highY
361         if .erase_button_area
362                 # Make erase area minutely larger
363                 .eraseLeft = .shiftLeftX - 0.01
364                 .eraseRight = .shiftRightX + 0.01
365                 .eraseBottom = .shiftLowY - 0.01
366                 .eraseTop = .shiftHighY + 0.01
367                 demo Paint rectangle... White .eraseLeft .eraseRight .eraseBottom .eraseTop
368         endif
369         
370     # If label starts with "$", it is a text field. Then do not draw the button
371         if not startsWith(.label$, "$")
372         # Give some depth to button: Draw flank outline
373                 if .shiftDown or .shiftX or .shiftY
374                         if .push <= 0
375                         demo Paint rounded rectangle... '.flankBackGroundColorUp$' .shiftLeftX .shiftRightX .shiftLowY .shiftHighY '.mm'
376                                 demo Colour... '.flankLineColorUp$'
377                         demo Line width... '.flankLineWidthUp'
378                         else
379                         demo Paint rounded rectangle... '.flankBackGroundColorDown$' .shiftLeftX .shiftRightX .shiftLowY .shiftHighY '.mm'
380                                 demo Colour... '.flankLineColorDown$'
381                         demo Line width... '.flankLineWidthDown'
382                         endif
383                 demo Draw rounded rectangle... .shiftLeftX .shiftRightX .shiftLowY .shiftHighY '.mm'
384                 endif
386                 # Button Down will shift the top perspective
388         # Draw the button top. Buttons with variable labels are treated differently
389                 if .push = 0 or (.push >= 2 and index(.buttonText$, "$$$"))
390                 demo Paint rounded rectangle... '.topBackGroundColorUp$' '.leftX' '.rightX' '.lowY' '.highY' '.mm'
391                         demo Colour... '.topLineColorUp$'
392                 demo Line width... '.topLineWidthUp'
393                 elsif .push < 0
394                 demo Paint rounded rectangle... '.topBackGroundColorDisabled$' '.leftX' '.rightX' '.lowY' '.highY' '.mm'
395                         demo Colour... '.topLineColorDisabled$'
396                 demo Line width... '.topLineWidthDisabled'
397                 else
398                         # Button Down
399                         .leftX += .shiftDown
400                         .rightX += .shiftDown
401                         .lowY -= .shiftDown
402                         .highY -= .shiftDown
404                 demo Paint rounded rectangle... '.topBackGroundColorDown$' .leftX .rightX .lowY .highY '.mm'
405                         demo Colour... '.topLineColorDown$'
406                 demo Line width... '.topLineWidthDown'
407                 endif
408         demo Draw rounded rectangle... '.leftX' '.rightX' '.lowY' '.highY' '.mm'
409         endif
410    
411     # The button text and symbol
412         .horWC = demo Horizontal mm to wc... 10.0
413         .verWC = demo Vertical mm to wc... 10.0
414         if .verWC > 0
415                 .verCoeff = .horWC / .verWC
416         else
417                 .verCoeff = 1
418         endif
420     .centerX = (.leftX + .rightX)/2
421     .centerY = .lowY + 0.6*(.highY-.lowY)
422     .radius = min(.verCoeff * (.highY - .lowY ), (.rightX - .leftX))/3
423         .buttonKey$ = replace$(.buttonKey$, "\", "\\", 0)
424         .buttonKey$ = replace$(.buttonKey$, """", "\""""", 0)
425         .keyText$ = replace$(.buttonKey$, "\", "", 0)
426         .keyText$ = replace$(.keyText$, "-", "", 0)
427         .newText$ = ""
428         if .keyText$ <> ""
429                 .newText$ = replace_regex$(.buttonText$, "['.keyText$']", "#%&", 1)
430         endif
431         if .newText$ = ""
432                 .newText$ = .buttonText$
433         endif
434         # Variable text field, read corresponding variable
435         if index(.newText$, "$$$")
436                 .fieldName$ = replace_regex$(.label$, "^[!$#]", "", 0)
437                 .fieldName$ = replace_regex$(.fieldName$, "^(.)", "\l\1", 0)
438                 .varPrefix$ = replace_regex$(.table$, "^(.)", "\l\1", 0)
439                 .newText$ = replace$(.newText$, "$$$", '.varPrefix$'.'.fieldName$'$, 0)
440         endif
441         if .push = 1 or .push = -1
442                 demo Grey
443                 if .buttonColor$ = "Red"
444                         .buttonColor$ = "Pink"
445                 elsif .buttonColor$ = "Blue"
446                         .buttonColor$ = "{0.5,0.5,1}"
447                 else
448                         .buttonColor$ = "Grey"
449                 endif
450     elsif .push >= 2
451         .buttonColor$ = "Maroon"
452         else
453         demo Colour... Black
454         endif
456     call '.buttonDraw$' '.buttonColor$' '.centerX' '.centerY' '.radius' 
457         call set_font_size '.buttonFontSize'
458     demo Colour... '.buttonColor$'
459         if .rotation = 0
460                 .anchorY = .lowY
461                 .verticalAlignment$ = "Bottom"
462         else
463                 .anchorY = .lowY + 0.5*(.highY-.lowY)
464                 .verticalAlignment$ = "Half"
465         endif
466     demo Text special... '.centerX' Centre '.anchorY' '.verticalAlignment$' 'defaultFont$' '.buttonFontSize' '.rotation' '.newText$'
467         demoShow()
469         # Reset
470         call set_font_size 'defaultFontSize'
471     demo Black
472     demo Line width... 'defaultLineWidth'
473     
474     label NOBUTTON
475 endproc
477 procedure set_window_title .table$ .addedText$
478     select Table '.table$'
479     .row = Search column... Label !WindowTitle
480         if .row < 1
481                 call emergency_table_exit Button Table '.table$' does not have a row with label !WindowTitle
482         endif
483         .windowText$ = Get value... '.row' Text
484         call convert_praat_to_latin1 '.windowText$'
485         .windowText$ = convert_praat_to_latin1.text$
487         if index(.windowText$, "$$$")
488                 .displayWindowText$ = replace$(.windowText$, "$$$", .addedText$, 0)
489         else
490                 .displayWindowText$ = .windowText$ + .addedText$
491         endif
492         
493     demoWindowTitle(.displayWindowText$ )
494 endproc
496 # Handle language setting 
497 procedure processLanguageCodes .table$ .label$
498         .table$ = "Config"
499     call Draw_button 'config$' Language_'config.language$' 0
500     call Draw_button 'config$' '.label$' 2
501     # Someone might have to use more than 2 chars for the language code
502     .numChars = length(.label$) - length("Language_")
503         .lang$ = right$(.label$, .numChars)
504     # Load new tables
505     call set_language '.lang$'
506 endproc
508 # Set the language
509 procedure set_language .lang$
510         .redraw_config = 0
511     # Remove old tables
512     if buttons$ <> ""
513         select Table 'buttons$'
514         Remove
515                 .redraw_config = 1
516     endif
517     if config$ <> ""
518         select Table 'config$'
519         Remove
520                 .redraw_config = 1
521     endif
522     
523     # See whether there is a custom language
524     sgc.customLanguage$ = ""
525     .langList = nocheck Create Strings as file list: "CustomLanguages", preferencesTableDir$+"/Config_*.Table"
526     # NOTE: The list might not exist!!!
527     if .langList = undefined
528                 .numLanguages = -1
529         else
530                 .numLanguages = Get number of strings
531         endif
532     if .numLanguages <= 0
533                 if not .langList = undefined
534                         Remove
535                 endif
536                 .langList = nocheck Create Strings as file list: "CustomLanguages", globaltablelists$+"/Config_*.Table"
537                 if .langList = undefined
538                         .numLanguages = -1
539                 else
540                         .numLanguages = Get number of strings
541                 endif
542         endif
543     if .numLanguages > 0
544                 .configTable$ = Get string: 1
545                 .startChar = rindex(.configTable$, "_")
546                 sgc.customLanguage$ = right$(.configTable$, length(.configTable$) - .startChar)
547                 sgc.customLanguage$ = left$(sgc.customLanguage$, index(sgc.customLanguage$, ".") -1)
548     endif
549     if not .langList = undefined
550                 select .langList
551                 Remove
552     endif
553     
554     # Set language
555         call checkTable 'configTableName$'_'.lang$'
556         if checkTable.available
557                 config.language$ = .lang$
558         else
559                 config.language$ = "EN"
560         endif
561         
562         if config.language$ = "JA"
563                 CJK font style preferences: "Japanese"
564         else
565                 CJK font style preferences: "Chinese"
566         endif
567     
568     # Load buttons tables
569     call loadTable 'buttonsTableName$'
570     buttons$ = selected$("Table")
571     Append column... Text
572     Append column... Key
573     Append column... Helptext
574     .numLabels = Get number of rows
575     call testLoadTable 'buttonsTableName$'_'config.language$'
576     if testLoadTable.table > 0   
577                 call loadTable 'buttonsTableName$'_'config.language$'
578         else
579                 call loadTable 'buttonsTableName$'_EN
580         endif
581     .buttonsLang$ = selected$("Table")
582     for .row to .numLabels
583                 select Table 'buttons$'
584                 .label$ = Get value... '.row' Label
585         call findLabel '.buttonsLang$' '.label$'
586             if findLabel.row > 0
587             select Table '.buttonsLang$'
588                 .valueText$ = Get value... 'findLabel.row' Text
589                 .valueKey$ = Get value... 'findLabel.row' Key
590                 .valueHelp$ = Get value... 'findLabel.row' Helptext
591                 select Table 'buttons$'
592                 Set string value... '.row' Text '.valueText$'
593                 Set string value... '.row' Key '.valueKey$'
594                 Set string value... '.row' Helptext '.valueHelp$'
595                 elsif index(.label$, "_")
596                         # Load alternative language table
597                         .startChar = rindex(.label$, "_")
598                         .otherLanguage$ = right$(.label$, length(.label$) - .startChar)
599                         call loadTable 'buttonsTableName$'_'.otherLanguage$'
600                 .otherbuttonsLang$ = selected$("Table")
601                 call findLabel '.otherbuttonsLang$' '.label$'
602                 if findLabel.row > 0
603                 select Table '.buttonsLang$'
604                         .valueText$ = Get value... 'findLabel.row' Text
605                         .valueKey$ = Get value... 'findLabel.row' Key
606                         .valueHelp$ = Get value... 'findLabel.row' Helptext
607                         select Table 'buttons$'
608                         Set string value... '.row' Text '.valueText$'
609                         Set string value... '.row' Key '.valueKey$'
610                         Set string value... '.row' Helptext '.valueHelp$'
611                 else
612                 call emergency_table_exit Cannot find Label: '.otherbuttonsLang$' '.label$'
613                 endif
614                         select Table '.otherbuttonsLang$'
615                         Remove
616         else
617             call emergency_table_exit Cannot find Label: '.buttonsLang$' '.label$'
618         endif
619     endfor
620     select Table '.buttonsLang$'
621     Remove
622     
623     # Load configuration table
624     call loadTable 'configTableName$'
625     config$ = selected$("Table")
626     .configTable = selected()
627         # Substitute or remove optional languages
628         .optRow = Search column: "Label", "!Language_???"
629         if .optRow > 0
630                 .row = -1
631                 if sgc.customLanguage$ <> ""
632                         .row = Search column: "Label", "Language_"+sgc.customLanguage$
633                 endif
634                 if .row <= 0 and sgc.customLanguage$ <> ""
635                         Set string value: .optRow, "Label", "Language_"+sgc.customLanguage$
636                 else
637                         .tableLength = Get number of rows
638                         # Should never ever happen
639                         if .tableLength > 1
640                                 Remove row: .optRow
641                         else
642                                 Set string value: .optRow, "Label", "Language_EN"
643                         endif
644                 endif
645         endif
646     
647     select .configTable
648     Append column... Text
649     Append column... Key
650     Append column... Helptext
651     .numLabels = Get number of rows
652     call testLoadTable 'configTableName$'_'config.language$'
653     if testLoadTable.table > 0   
654                 call loadTable 'configTableName$'_'config.language$'
655         else
656                 call loadTable 'configTableName$'_EN
657         endif
658     .configLang$ = selected$("Table")
659     for .row to .numLabels
660                 select Table 'config$'
661                 .label$ = Get value... '.row' Label
662         call findLabel '.configLang$' '.label$'
663             if findLabel.row > 0
664             select Table '.configLang$'
665                 .valueText$ = Get value... 'findLabel.row' Text
666                 .valueKey$ = Get value... 'findLabel.row' Key
667                 .valueHelp$ = Get value... 'findLabel.row' Helptext
668                 select Table 'config$'
669                 Set string value... '.row' Text '.valueText$'
670                 Set string value... '.row' Key '.valueKey$'
671                 Set string value... '.row' Helptext '.valueHelp$'
672                 elsif index(.label$, "_")
673                         .startChar = rindex(.label$, "_")
674                         .otherLanguage$ = right$(.label$, length(.label$) - .startChar)
675                         call loadTable 'configTableName$'_'.otherLanguage$'
676                 .otherconfigLang$ = selected$("Table")
677                 call findLabel '.otherconfigLang$' '.label$'
678                 if findLabel.row > 0
679                 select Table '.otherconfigLang$'
680                         .valueText$ = Get value... 'findLabel.row' Text
681                         .valueKey$ = Get value... 'findLabel.row' Key
682                         .valueHelp$ = Get value... 'findLabel.row' Helptext
683                         select Table 'config$'
684                         Set string value... '.row' Text '.valueText$'
685                         Set string value... '.row' Key '.valueKey$'
686                         Set string value... '.row' Helptext '.valueHelp$'
687                 else
688                 call emergency_table_exit Cannot find Label: '.otherconfigLang$' '.label$'
689                 endif
690                         select Table '.otherconfigLang$'
691                         Remove
692         else
693             call emergency_table_exit Cannot find Label: '.configLang$' '.label$'
694         endif
695     endfor
696     select Table '.configLang$'
697     Remove
699         # Make language change visible
700         if .redraw_config
701                 call Draw_config_page
702         endif
704 endproc
706 ###############################################################
708 # Button Drawing Routines
710 ###############################################################
712 # A stub for buttons that do not have a drawing routine (yet)
713 procedure DrawNull .color$ .x .y .size
714 endproc
716 procedure DrawHelp .color$ .x .y .size
717         .currentFontSize = 24
718         .y -= .size
719         .maxHeight = 2*.size
720         call adjustFontSizeOnHeight 'defaultFont$' '.currentFontSize' '.maxHeight'
721         .currentFontSize = adjustFontSizeOnHeight.currentFontSize
722         call set_font_size '.currentFontSize'
723         demo Colour... '.color$'
724         demo Text... '.x' Centre '.y' Bottom ?
725         call set_font_size 'defaultFontSize'
726 endproc
728 ###############################################################
730 # Button Processing Routines
732 ###############################################################
734 # Search row in table on label
735 procedure findKey .table$ .label$
736         .row = 0
737         select Table '.table$'
738         .to$ = selected$("Table")
739         .to$ = "Table_"+.to$
740         .numRows = Get number of rows
741         for .i to .numRows
742                 .currentKey$ = '.to$'$[.i, "Key"]
743                 if .label$ = .currentKey$
744                         .row = .i
745                         goto KEYFOUND
746                 endif
747         endfor
748         label KEYFOUND
749         if .row <= 0 and index(.label$, "_") <= 0
750                 printline "'.label$'" is not a key in '.table$'
751         endif
752 endproc
754 procedure findLabel .table$ .label$
755         .row = 0
756         select Table '.table$'
757         .to$ = selected$("Table")
758         .to$ = "Table_"+.to$
759         .numRows = Get number of rows
760         for .i to .numRows
761                 .currentKey$ = '.to$'$[.i, "Label"]
762                 if .label$ = .currentKey$
763                         .row = .i
764                         goto LABELFOUND
765                 endif
766         endfor
767         label LABELFOUND
768         if .row <= 0 and index(.label$, "_") <= 0
769                 call emergency_table_exit "'.label$'" is not a key in '.table$'
770         endif
771 endproc
773 # Get the label
774 procedure buttonClicked table$ .x .y
775         .label$ = ""
776         select Table 'table$'
777         .bo$ = selected$("Table")
778         .bo$ = "Table_"+.bo$
779         .numRows = Get number of rows
780         for .i to .numRows
781                 if .label$ = ""
782                         .leftX = '.bo$'[.i, "LeftX"]
783                         .rightX = '.bo$'[.i, "RightX"]
784                         .lowY = '.bo$'[.i, "LowY"]
785                         .highY = '.bo$'[.i, "HighY"]
786                         if .x > .leftX and .x < .rightX and .y > .lowY and .y < .highY
787                                 .label$ = '.bo$'$[.i, "Label"]
788                         endif
789                 endif
790         endfor
791 endproc
793 procedure keyPressed table$ .pressed$
794         .label$ = ""
795         # Magic
796         if .pressed$ = "" and not demoShiftKeyPressed()
797                 .label$ = "Refresh"
798         endif
799         .lowerPressed$ = replace_regex$(.pressed$, ".", "\L&", 0)
800         .upperPressed$ = replace_regex$(.pressed$, ".", "\U&", 0)
801         select Table 'table$'
802         .bo$ = selected$("Table")
803         .bo$ = "Table_"+.bo$
804         .numRows = Get number of rows
805         for .i to .numRows
806                 if .label$ = ""
807                         .key$ = '.bo$'$[.i, "Key"]
808                         if index(.key$, .lowerPressed$) or index(.key$, .upperPressed$)
809                                 .label$ = '.bo$'$[.i, "Label"]
810                         endif
811                 endif
812         endfor
813 endproc
815 procedure count_syllables
816         .number = 0
817         .pinyin$ = ""
818         select sgc.currentWordlist
819         if sgc.currentWord > 0 and sgc.currentWord <= sgc.numberOfWords
820                 .sound$ = Get value... 'sgc.currentWord' Sound
821                 call readPinyin 'sgc.currentWord'
822                 .pinyin$ = readPinyin.pinyin$
823         endif
824         call add_missing_neutral_tones '.pinyin$'
825         .pinyin$ = add_missing_neutral_tones.pinyin$
826         if index_regex(.pinyin$, "[0-9]") > 0
827                 .number = length(replace_regex$(.pinyin$, "[^\d]+([\d]+)", "1", 0))
828         elsif .pinyin$ <> ""
829                 .number = 1
830         endif
831 endproc
833 procedure play_sound .sound
834     if .sound > 0
835                 if sgc.useAlternativePlayer and fileReadable(sgc.playCommandFile$)
836                         .scratchFile$ = "'sgc.scratchAudioDir$'SCRATCH.wav"
837                         select .sound
838                         Save as WAV file: .scratchFile$
839                         .command$ < 'sgc.playCommandFile$'
840                         .command$ = replace$(.command$, "[']", """", 0)
841                         .command$ = replace$(.command$, "'newline$'", " ", 0)
842                         if unix or macintosh
843                                 system_nocheck bash -c -- ''.command$' "'.scratchFile$'"'
844                         elsif windows
845                                 system_nocheck call '.command$' "'.scratchFile$'"
846                         endif
847                         deleteFile(.scratchFile$)
848                 else
849                         select .sound
850                         Play
851         endif
852     endif
853 endproc
855 procedure record_sound .recordingTime
856         if .recordingTime <= 0
857                 .recordingTime = recordingTime
858         endif
859         call clean_up_sound
860         
861         # NOTE: Some sound can be playing! This will not be stopped.
862         
863         # There is a very nasty delay before the first recording starts, do a dummy record
864         if not variableExists("recordingInitialized") and not sgc.useAlternativeRecorder
865                 call basic_sound_recording 'samplingFrequency' 0.1
866                 Remove
867                 recordingInitialized = 1
868         endif
869         # Recording light
870     demo Paint circle... Red 5 95 2
871     demoShow()
872     
873         # In Windows XP interaction between demoWaitForInput and Record Sound blocks drawing the feedback
874         # This code might be removed #
875         if windows and endsWith(build_SHA$, " XP")
876                 # Display a pause window to flush the graphics buffer
877                 beginPause ("DESTROY WINDOW ")
878                         comment (" ")
879                 endPause ("DESTROY WINDOW ", 1)
880         #call init_window
881         demo Paint circle... Red 5 95 2
882         demoShow()
883         endif
884         ##############################
885         call basic_sound_recording 'samplingFrequency' '.recordingTime'
887     demo Paint circle... White 5 95 2.5
888     call wipeArea 'wipeFeedbackArea$'
890     # Feedback on recording level
891     .extremum = Get absolute extremum... 0 0 None
892     .radius = 2 * .extremum
893     if .radius <= 0
894                 .radius = 0.02
895     endif
896     .blue = 0
897     .green = 0
898     .red = 0
899     if .extremum >= 0.95
900             .red = 1
901     elsif .extremum >= 0.49
902             .green = 1
903     else
904             .green = .extremum / 0.5
905     endif
906     .color$ = "{'.red','.green','.blue'}"
907     demo Colour... '.color$'
908     demo Line width... 1
909     demo Draw circle... 5 95 '.radius'
910     # Reset
911     demoShow()
912     demo Colour... Black
913     demo Line width... 'defaultLineWidth'
914     # Process sound
915     Rename... Tmp
916     Resample... 10000 50
917     Rename... Pronunciation
918     recordedSound$ = selected$("Sound")
919     sgc.recordedSound = selected("Sound")
920     select Sound Tmp
921     Remove
922     select Sound 'recordedSound$'
923     sgc.recordedSound = selected("Sound")
924         
925     # Cut out real sound from silences/noise
926     call sound_detection 'recordedSound$' 'soundMargin'
927     select Sound 'recordedSound$'
928     sgc.recordedSound = selected("Sound")
929     
930     # Store audio if requested
931     if sgc.saveAudioOn and sgc.saveAudio$ <> ""
932                 if sgc.savePerf$ <> "" and fileReadable(sgc.savePerf$)
933                         .pinyin$ = ""
934                         select sgc.currentWordlist
935                         if sgc.currentWord > 0 and sgc.currentWord <= sgc.numberOfWords
936                                 call readPinyin 'sgc.currentWord'
937                                 .pinyin$ = readPinyin.pinyin$
938                                 .outputName$ = "'sgc.saveAudio$'/'.pinyin$'.wav"
939                                 select sgc.recordedSound
940                                 Save as WAV file: .outputName$
941                         endif
942                 else
943                         # The Audio directory disappeared
944                         sgc.savePerf$ = ""
945                         sgc.saveAudioOn = 0
946                         sgc.saveAudio$ = ""
947                         config.savePerf = 0
948                         config.openPerf = 0
949                         config.clearSummary = 0
950                         config.audioName$ = ""
951                 endif
952     endif
953     
954 endproc
956 # Uses global variables!!!
957 procedure basic_sound_recording .samplingFrequency .recordingTime
958         # Use a different recorder program
959     if sgc.useAlternativeRecorder and fileReadable(sgc.recordCommandFile$)
960                 .scratchFile$ = "'sgc.scratchAudioDir$'SCRATCH.wav"
961                 .command$ < 'sgc.recordCommandFile$'
962                 .command$ = replace$(.command$, "[']", """", 0)
963                 .command$ = replace$(.command$, "'newline$'", " ", 0)
964                 if unix or macintosh
965                         if unix and .recordingTime < 1
966                                 .recordingTime = 1
967                         endif
968                         system_nocheck bash -c -- ''.command$' '.recordingTime''
969                 elsif windows
970                         system_nocheck call '.command$' '.recordingTime'
971                 endif
972                 .sound = Read from file: .scratchFile$
973                 deleteFile(.scratchFile$)
974         else
975                 .sound = nocheck noprogress nowarn Record Sound (fixed time)... 'config.input$' 0.99 0.5 '.samplingFrequency' '.recordingTime'
976                 if .sound = undefined
977                         .sound = Create Sound: "Pronunciation", 0, .recordingTime, .samplingFrequency, "0"
978                 endif
979         endif
980         
981         # The recorded sound should now be the selected object!!!
982         select .sound
983 endproc
985 # Select real sound from recording
986 # Uses some global variable
987 procedure sound_detection .sound$ .margin
988         select Sound '.sound$'
989         .soundlength = Get total duration
990         .internalSilence = 2*.margin
991         
992         # Silence and remove noise, DANGEROUS
993         To TextGrid (silences)... 'minimumPitch' 0 'noiseThresshold' '.internalSilence' 0.1 silent sounding
994         Rename... Input'.sound$'
996         select TextGrid Input'.sound$'
997         .numberofIntervals = Get number of intervals... 1
998         if .numberofIntervals < 2
999                 .numberofIntervals = 0
1000         endif
1002         # The code below will suppress noise, but also weak third tones
1003         # This handles the problem that third tones can be realized with 
1004         # alternative cues, e.g, non-standard voice and very low levels 
1005         #
1006         # Remove buzzing and other obnoxious sounds (if switched on)
1007         for .i from 1 to .numberofIntervals
1008            select TextGrid Input'.sound$'
1009            .value$ = Get label of interval... 1 '.i'
1010            .begintime = Get starting point... 1 '.i'
1011            .endtime = Get end point... 1 '.i'
1012         
1013                 # Remove noise
1014                 if .value$ = "silent"
1015                         select Sound '.sound$'
1016                         Set part to zero... '.begintime' '.endtime' at nearest zero crossing
1017                 endif
1018         endfor
1020         # Select target sound
1021         .maximumIntensity = -1
1022         .counter = 1
1023         for i from 1 to .numberofIntervals
1024            select TextGrid Input'.sound$'
1026            .value$ = Get label of interval... 1 'i'
1027            .begintime = Get starting point... 1 'i'
1028            .endtime = Get end point... 1 'i'
1030            if .value$ != "silent"
1031            if .begintime > .margin
1032                   .begintime -= .margin
1033            else
1034                    .begintime = 0
1035            endif
1036            if .endtime + .margin < .soundlength
1037                    .endtime += .margin
1038            else
1039                    .endtime = .soundlength
1040            endif
1042            select Sound '.sound$'
1043            Extract part... '.begintime' '.endtime' Rectangular 1.0 no
1044            Rename... Tmp'.sound$'
1045            Subtract mean
1046            .newIntensity = Get intensity (dB)
1047            if .newIntensity > .maximumIntensity
1048                    if .maximumIntensity > 0
1049                    select Sound New'.sound$'
1050                    Remove
1051                    endif
1052                    select Sound Tmp'.sound$'
1053                    Rename... New'.sound$'
1054                    .maximumIntensity = .newIntensity
1055            else
1056                    select Sound Tmp'.sound$'
1057                    Remove
1058            endif
1059            # 
1060            endif
1061         endfor
1062         if .maximumIntensity > minimumIntensity
1063                 select Sound '.sound$'
1064                 Remove
1065                 select Sound New'.sound$'
1066                 Rename... '.sound$'
1067         elsif .maximumIntensity > -1
1068                 select Sound New'.sound$'
1069                 Remove
1070         endif
1071         select TextGrid Input'.sound$'
1072         Remove
1073         
1074         select Sound '.sound$'
1075 endproc
1077 procedure end_program
1078         call write_preferences "" 
1079         demo Erase all
1080         select all
1081         Remove
1082         exit
1083 endproc
1085 ######################################################
1087 # Configuration Page
1089 ######################################################
1090 procedure config_page
1091     demo Erase all
1092     demoWindowTitle("Speak Good Chinese: Change settings")
1093     .label$ = ""
1094     call Draw_config_page
1095     
1096         goto GOBACK index_regex(replaySGC2Log$, "^\s*#") <= 0
1098     while (.label$ <> "Return") and demoWaitForInput() 
1099                 .clickX = -1
1100                 .clickY = -1
1101                 .pressed$ = ""
1102             .label$ = ""
1103             if demoClicked()
1104                     .clickX = demoX()
1105                     .clickY = demoY()
1106                     call buttonClicked 'config$' '.clickX' '.clickY'
1107                     .label$ = buttonClicked.label$
1108             elsif demoKeyPressed()
1109                     .pressed$ = demoKey$()
1110                     call keyPressed 'config$' '.pressed$'
1111                     .label$ = keyPressed.label$
1112             endif
1114                 # You cannot select a text field
1115                 if startsWith(.label$, "$")
1116                         .label$ = ""
1117                 endif
1118                 
1119             # Do things
1120             if .label$ != ""
1121                     # Handle push button in process_config
1122                     call process_config '.label$' '.clickX' '.clickY' '.pressed$'
1123             endif
1124         
1125         if .label$ = "Return"
1126             goto GOBACK
1127         endif
1128     endwhile
1130     # Go back
1131     label GOBACK
1132     call init_window
1133 endproc
1135 procedure Draw_config_page
1136         demo Erase all
1137         # Draw background
1138         if config.showBackground
1139                 call draw_background Background
1140         endif
1141         # Draw buttons
1142     call Draw_all_buttons 'config$'
1143         call set_window_title 'config$'  
1144     # Set correct buttons (alert)
1145         call setConfigMainPage
1146 endproc
1148 # Do what is asked
1149 procedure process_config .label$ .clickX .clickY .pressed$
1150         if .label$ <> "" and not startsWith(.label$,"!")
1151                 .label$ = replace_regex$(.label$, "^[#]", "", 0)
1152                 .label$ = replace$(.label$, "_", " ", 0)
1153                 
1154                 # Log activity
1155                 'sgc2.logging$' call process'config$''.label$' '.clickX' '.clickY' '.pressed$'
1156                 
1157                 call process'config$''.label$' '.clickX' '.clickY' '.pressed$'
1158         endif
1159 endproc
1161 ###############################################################
1163 # Presenting help texts
1165 ###############################################################
1167 # Process Help
1168 procedure help_loop .table$ .redrawProc$
1169         # General Help text
1170         call  write_help_title '.table$'
1171         
1173     .label$ = ""
1174     call Draw_button '.table$' Help 2
1175         goto HELPGOBACK index_regex(replaySGC2Log$, "^\s*#") <= 0
1176         
1177     .redrawScreen = 0
1178     while (.label$ <> "Help") and demoWaitForInput() 
1179             .label$ = ""
1180             if demoClicked()
1181                     .clickX = demoX()
1182                     .clickY = demoY()
1183                     call buttonClicked '.table$' '.clickX' '.clickY'
1184                     .label$ = buttonClicked.label$
1185             elsif demoKeyPressed()
1186                     .pressed$ = demoKey$()
1187                     call keyPressed '.table$' '.pressed$'
1188                     .label$ = keyPressed.label$
1189             endif
1191             if .label$ != "" and .label$ <> "Help"
1192                         # Redraw screen
1193                         if .redrawScreen
1194                                 demo Erase all
1195                                 call '.redrawProc$'
1196                         else
1197                         .redrawScreen = 1
1198                         endif
1199                         call Draw_button '.table$' Help 2
1200                         call  write_help_title '.table$'
1202                     # Handle push button in process_config
1203                     call write_help_text '.table$' '.label$'
1204                     
1205                         # Log activity
1206                         'sgc2.logging$' demo Erase all
1207                         'sgc2.logging$' call '.redrawProc$'
1208                         'sgc2.logging$' call write_help_title '.table$'
1209                         'sgc2.logging$' call write_help_text '.table$' '.label$'
1210             endif
1211         
1212     endwhile
1213     
1214         label HELPGOBACK
1215         # Reset button
1216     call Draw_button '.table$' Help 0
1217         demo Erase all
1218         call '.redrawProc$'
1219 endproc
1221 # Write help text
1222 procedure write_help_text .table$ .label$
1223         call findLabel '.table$' '.label$'
1224         .row = findLabel.row
1225         select Table '.table$'
1226         # Get text
1227         if .row <= 0
1228                 call findLabel '.table$' Help
1229                 .row = findLabel.row
1230                 select Table '.table$'
1231         endif
1232         .helpText$ = Get value... '.row' Helptext
1233         .helpKey$ = Get value... '.row' Key
1234         .helpKey$ = replace$(.helpKey$, "\", "", 0)
1235         .helpKey$ = replace$(.helpKey$, "_", "\_ ", 0)
1236         if index_regex(.helpKey$, "\S")
1237                 .helpText$ = .helpText$+" ("+.helpKey$+")"
1238         endif
1239         # Get button values
1240     .leftX = Get value... '.row' LeftX
1241     .rightX = Get value... '.row' RightX
1242     .lowY = Get value... '.row' LowY
1243     .highY = Get value... '.row' HighY
1244         
1245         # PopUp dimensions
1246         .currentHelpFontSize = defaultFontSize
1247     call set_font_size '.currentHelpFontSize'
1248         .helpTextSize = demo Text width (wc)... '.helpText$'
1249         .helpTextSize += 4
1250         if .leftX > 50
1251                 .htXleft = 20
1252                 .htXright = .htXleft + .helpTextSize + 5
1253                 .xstart = .leftX
1254         else
1255                 .htXright = 80
1256                 .htXleft = .htXright - .helpTextSize - 5
1257                 .xstart = .rightX
1258         endif
1259         if .lowY > 50
1260                 .htYlow = 40
1261                 .htYhigh = .htYlow + 7
1262                 .ystart = .lowY
1263                 .yend = .htYhigh
1264         else
1265                 .htYhigh = 60
1266                 .htYlow = .htYhigh - 7
1267                 .ystart = .highY
1268                 .yend = .htYlow
1269         endif
1271         # Adapt font size to horizontal dimensions
1272         .maxWidth = 90
1273         call adjustFontSizeOnWidth 'defaultFont$' '.currentHelpFontSize' '.maxWidth' '.helpText$'
1274         .currentHelpFontSize = adjustFontSizeOnWidth.newFontSize
1275         if .htXleft < 0 or .htXright > 100
1276                 .htXleft = 0
1277                 .htXright = .htXleft + adjustFontSizeOnWidth.textWidth + 5
1278         endif
1279         call set_font_size '.currentHelpFontSize'
1281         # Adapt vertical dimensions to font height
1282         call points_to_wc '.currentHelpFontSize'
1283         .lineHeight = points_to_wc.wc
1284         if .lineHeight > .htYhigh - .htYlow - 4
1285                 .htYhigh = .htYlow + .lineHeight + 4
1286         endif
1288         # Determine arrow endpoints
1289         .xend = .htXleft
1290         if abs(.htXleft - .xstart) > abs(.htXright - .xstart)
1291                 .xend = .htXright
1292         endif
1293         if abs((.htXleft+.htXright)/2 - .xstart) < min(abs(.htXright - .xstart),abs(.htXleft - .xstart))
1294                 .xend = (.htXleft+.htXright)/2
1295         endif
1296         
1297         .xtext = .htXleft + 2
1298         .ytext = .htYlow + 1
1299         
1300         # Draw pop-up
1301         .mm2wc = demo Horizontal mm to wc... 1
1302         .lineWidth = 2/.mm2wc
1303         demo Line width... '.lineWidth'
1304         demo Arrow size... '.lineWidth'
1305         demo Colour... 'sgc2.popUp_bordercolor$'
1306         demo Paint rectangle... 'sgc2.popUp_backgroundcolor$' '.htXleft' '.htXright' '.htYlow' '.htYhigh'
1307         demo Draw rectangle... '.htXleft' '.htXright' '.htYlow' '.htYhigh'
1308         demo Draw arrow... '.xstart' '.ystart' '.xend' '.yend'
1309         demo Line width... 'defaultLineWidth'
1310         demo Arrow size... 1
1311         demo Black
1312         demo Text... '.xtext' Left '.ytext' Bottom '.helpText$'
1313         demoShow()
1314         call set_font_size 'defaultFontSize'
1315         
1316 endproc
1318 procedure write_help_title .table$
1319         # Set help text title
1320         # General Help text
1321         call findLabel '.table$' Help
1322         .row = findLabel.row
1323         select Table '.table$'
1324         .helpTitle$ = Get value... '.row' Helptext
1325         .helpKey$ = Get value... '.row' Key
1326         .helpKey$ = replace$(.helpKey$, "\", "", 0)
1327         .helpKey$ = replace$(.helpKey$, "_", "\_ ", 0)
1328         .helpTitle$ = .helpTitle$+" ("+.helpKey$+")"
1329         
1330         call reset_viewport
1331         .helpTitleFontSize = 14
1332         # Adapt size of button to length of text
1333         .maxWidth = 80
1334         call adjustFontSizeOnWidth 'defaultFont$' '.helpTitleFontSize' '.maxWidth' '.helpTitle$'
1335         .helpTitleFontSize = adjustFontSizeOnWidth.newFontSize
1336         call set_font_size '.helpTitleFontSize'
1337         .helpTop = 100
1338         
1339         demo Select inner viewport... 0 100 0 100
1340         demo Axes... 0 100 0 100
1341         demo Text... 50 Centre '.helpTop' Top '.helpTitle$'
1342     call set_font_size 'defaultFontSize'
1343         call reset_viewport
1344 endproc
1346 ###############################################################
1348 # Miscelaneous procedures
1350 ###############################################################
1351 procedure printPageToPrinter
1352         call print_window
1353         demo Print... 'printerName$' 'printerPresets$'
1354         call init_window
1355 endproc
1357 procedure points_to_wc .points
1358         .mm = .points * 0.3527777778
1359         .wc = demo Vertical mm to wc... '.mm'
1360 endproc
1362 procedure reset_viewport
1363         .low = viewportMargin
1364         .high = 100 - viewportMargin
1365         demo Select inner viewport... '.low' '.high' '.low' '.high'
1366         demo Axes... 0 100 0 100
1367 endproc
1369 procedure set_font_size .fontSize
1370         call reset_viewport
1371         demo Font size... '.fontSize'
1372         call reset_viewport
1373 endproc
1375 procedure wipeArea .areaCommand$
1376         call reset_viewport
1377         '.areaCommand$'
1378 endproc
1380 procedure adjustFontSizeOnWidth .font$ .currentFontSize .maxWidth .text$
1381         demo '.font$'
1382         call set_font_size '.currentFontSize'
1383         .textWidth = demo Text width (wc)... '.text$'
1384         while .textWidth > .maxWidth and .currentFontSize > 2
1385                 .currentFontSize -= 0.5
1386                 call set_font_size '.currentFontSize'
1387                 .textWidth = demo Text width (wc)... '.text$'
1388         endwhile
1389         .diff = .textWidth - .maxWidth
1390         .newFontSize = .currentFontSize 
1391         demo 'defaultFont$'
1392 endproc
1394 procedure adjustRotatedFontSizeOnBox .font$ .currentFontSize .maxWidth .maxHeight .rotation .text$
1395         demo '.font$'
1396         .radians = .rotation/360 * 2 * pi
1397         .horWC = demo Horizontal mm to wc... 10.0
1398         .verWC = demo Vertical mm to wc... 10.0
1399         if .horWC > 0
1400                 .verCoeff = .verWC / .horWC
1401         else
1402                 .verCoeff = 1
1403         endif
1404         call set_font_size '.currentFontSize'
1405         .textLength = demo Text width (wc)... '.text$'
1406         while (.textLength * .verCoeff * sin(.radians) > .maxHeight or .textLength * cos(.radians) > .maxWidth) and .currentFontSize > 2
1407                 .currentFontSize -= 0.5
1408                 call set_font_size '.currentFontSize'
1409                 .textLength = demo Text width (wc)... '.text$'
1410         endwhile
1411         .diff = .textLength - .maxHeight
1412         .newFontSize = .currentFontSize 
1413         demo 'defaultFont$'
1414 endproc
1416 procedure adjustFontSizeOnHeight .font$ .currentFontSize .maxHeight
1417         demo '.font$'
1418         call points_to_wc '.currentFontSize'
1419         .lineHeight = points_to_wc.wc
1420         while .lineHeight > .maxHeight and .currentFontSize > 2
1421                 .currentFontSize -= 0.5
1422                 call points_to_wc '.currentFontSize'
1423                 .lineHeight = points_to_wc.wc
1424         endwhile
1425         .diff = .lineHeight - .maxHeight
1426         .newFontSize = .currentFontSize
1427         demo 'defaultFont$'
1428 endproc
1430 # Load a table with button info etc.
1431 # Load local tables if present. Else load
1432 # build-in scripted tables
1433 procedure loadTable .tableName$
1434         .tableVariableName$ = replace_regex$(.tableName$, "[^\w]", "_", 0);
1435         # Search for the table in local, preference, and global directories
1436         if localTableDir$ <> "" and fileReadable("'localTableDir$'/'.tableName$'.Table")
1437         .table = Read from file... 'localTableDir$'/'.tableName$'.Table
1438         elsif fileReadable("'preferencesTableDir$'/'.tableName$'.Table")
1439         .table = Read from file... 'preferencesTableDir$'/'.tableName$'.Table
1440         elsif fileReadable("'globaltablelists$'/'.tableName$'.Table")
1441         .table = Read from file... 'globaltablelists$'/'.tableName$'.Table
1442         # Load them from script
1443         elsif variableExists("procCreate'.tableVariableName$'$")
1444                 call Create'.tableVariableName$'
1445         Rename: .tableName$
1446                 .table = selected("Table")
1447         else
1448                 call emergency_table_exit '.tableName$' cannot be found
1449         endif
1450         
1451         # Check whether this is a real table
1452         selectObject: .table
1453         .fullName$ = selected$ ()
1454         .type$ = extractWord$(.fullName$, "")
1455         if .type$ <> "Table"
1456                 Remove
1457                 .table = -1
1458         endif
1460         if .table <= 0
1461                 call emergency_table_exit '.tableFileName$' corrupted or cannot be found
1462         endif
1463 endproc
1465 procedure testLoadTable .tableName$
1466         .table = 0
1467         .tableVariableName$ = replace_regex$(.tableName$, "[^\w]", "_", 0);
1468         # Search for the table in local, preference, and global directories
1469         if localTableDir$ <> "" and fileReadable("'localTableDir$'/'.tableName$'.Table")
1470         .table = 1
1471         elsif fileReadable("'preferencesTableDir$'/'.tableName$'.Table")
1472         .table = 2
1473         elsif fileReadable("'globaltablelists$'/'.tableName$'.Table")
1474         .table = 3
1475         # Load them from script
1476         elsif variableExists("procCreate'.tableVariableName$'$")
1477                 .table = 4
1478         else
1479                 .table = 0
1480         endif
1481 endproc
1483 procedure checkTable .tableName$
1484         .available = 0
1485         if localTableDir$ <> "" and fileReadable("'localTableDir$'/'.tableName$'.Table")
1486         .available = 1
1487         elsif fileReadable("'preferencesTableDir$'/'.tableName$'.Table")
1488         .available = 1
1489         elsif fileReadable("'globaltablelists$'/'.tableName$'.Table")
1490         .available = 1
1491         # Load them from script
1492         elsif variableExists("procCreate'.tableName$'$")
1493         .available = 1
1494         else
1495         .available = 0
1496     endif
1497 endproc
1499 # Create a pop-up window with text from a Text Table
1500 procedure write_text_table .table$
1501         .xleft = 10
1502         .xright = 90
1503         .ylow = 20
1504         .yhigh = 85
1505         .lineHeight = 2.5
1507         # Get table with text and longest line
1508         .numLines = 0
1509         call testLoadTable '.table$'
1510         if testLoadTable.table > 0
1511                 call loadTable '.table$'
1512                 .instructionText = selected()
1513                 .numLines = Get number of rows
1514         else
1515                 goto ESCAPEwrite_text_table
1516         endif
1517         .instructionFontSize = 14
1518         .referenceText$ = ""
1519         .maxlenght = 0
1520         .maxLine = 0
1521         .maxFontSize = 0
1522         .maxWidth = 0
1523         for .l to .numLines
1524                 select '.instructionText'
1525                 .currentText$ = Get value... '.l' text
1526                 # Expand variables, eg, 'praatVersion$'
1527                 call expand_praat_variables '.currentText$'
1528                 .currentText$ = expand_praat_variables.text$
1529                 
1530                 .font$ = Get value... '.l' font
1531                 .fontSize = Get value... '.l' size
1532                 call set_font_size '.fontSize'
1533                 .textWidth = demo Text width (wc)... '.currentText$'
1534                 if .fontSize > .maxFontSize
1535                         .maxFontSize = .fontSize
1536                 endif
1537                 if .textWidth > .maxWidth
1538                         .maxWidth = .textWidth
1539                         .instructionFontSize = .fontSize
1540                         .maxLine = .l
1541                 endif
1542         endfor
1543         select '.instructionText'
1544         .referenceText$ = Get value... '.maxLine' text
1545         .maxLineFont$ = Get value... '.maxLine' font
1546         .instructionFontSize = Get value... '.maxLine' size
1547         call set_font_size '.maxFontSize'
1548         
1549         # Adapt size of button to length of text
1550         .maxWidth = (.xright - .xleft) - 4
1551         .origFontSize = .instructionFontSize
1552         call adjustFontSizeOnWidth 'defaultFont$' '.instructionFontSize' '.maxWidth' '.referenceText$'
1553         call adjustFontSizeOnHeight 'defaultFont$' '.maxFontSize' '.lineHeight'
1554         .instructionFontSize = min(adjustFontSizeOnWidth.newFontSize, adjustFontSizeOnHeight.newFontSize)
1555         if adjustFontSizeOnWidth.diff > 0
1556                 .xright += adjustFontSizeOnWidth.diff/4
1557                 .xleft -= 3*adjustFontSizeOnWidth.diff/4
1558         endif
1559         call set_font_size '.instructionFontSize'
1560         .fontSizeFactor = .instructionFontSize / .origFontSize
1562         .numRows = Get number of rows
1563         # Calculate length from number of lines.
1564         .dy = .lineHeight
1565         .midY = .yhigh - (.yhigh - .ylow)/2
1566         .yhigh = .midY + (.numRows+1) * .dy / 2
1567         .ylow = .yhigh - (.numRows+1) * .dy
1568         .textleft = .xleft + 2
1569         
1570         demo Line width... 8
1571         demo Colour... 'sgc2.popUp_bordercolor$'
1572         demo Paint rectangle... 'sgc2.popUp_backgroundcolor$' '.xleft' '.xright' '.ylow' '.yhigh'
1573         demo Draw rectangle... '.xleft' '.xright' '.ylow' '.yhigh'
1574         demo Line width... 'defaultLineWidth'
1575         demo Black
1576         .ytext = .yhigh - 2 - .dy
1577         for .i to .numRows
1578                 select '.instructionText'
1579                 .font$ = Get value... '.i' font
1580                 .fontSize = Get value... '.i' size
1581                 .font$ = extractWord$(.font$, "")
1582                 # Scale font
1583                 .fontSize = floor(.fontSize*.fontSizeFactor)
1584                 if .fontSize < 4
1585                         .fontSize = 4
1586                 endif
1587                 .line$ = Get value... '.i' text
1588                 # Expand variables, eg, 'praatVersion$'
1589                 call expand_praat_variables '.line$'
1590                 .line$ = expand_praat_variables.text$
1591                 
1592                 # Display text
1593                 demo Text special... '.textleft' Left '.ytext' Bottom '.font$' '.fontSize' 0 '.line$'
1594                 .ytext -= .dy
1595         endfor  
1596         demoShow()      
1597         call set_font_size 'defaultFontSize'
1598         
1599         select '.instructionText'
1600         Remove
1601         
1602         label ESCAPEwrite_text_table
1603 endproc
1606 # Create a pop-up window with text from an existing Table object
1607 procedure write_tabbed_table .table$ .labelTextTable$
1608         .xleft = 0
1609         .xright = 100
1610         .ylow = 20
1611         .yhigh = 85
1612         .lineHeight = 2.5
1614         # Get table with text and longest line
1615         call testLoadTable '.table$'
1616         if testLoadTable.table <= 0
1617                 call loadTable '.labelTextTable$'
1618                 .labelText$ = selected$("Table")
1619         endif
1620                 
1621         select Table '.table$'
1622         .tabbedText = selected()
1623         .numLines = Get number of rows
1624         .numCols = Get number of columns
1625         .font$ = defaultFont$
1626         .fontSize = defaultFontSize
1627         # Standard width
1628         .widthCanvas = .xright - .xleft
1629         .dx = (.widthCanvas - 4) / (.numCols)
1631         # Get longest entry
1632         demo '.font$'
1633         call set_font_size '.fontSize'
1634         .maxWidth = 0
1635         for .i from 0 to .numLines
1636                 .xtext = .xleft + .dx / 2
1637                 for .j to .numCols
1638                         select '.tabbedText'
1639                         .currentLabel$ = Get column label... '.j'
1640                         if .i > 0
1641                                 .line$ = Get value... '.i' '.currentLabel$'
1642                         else
1643                                 .line$ = .currentLabel$
1644                                 select Table '.labelText$'
1645                         call findLabel '.labelText$' '.line$'
1646                         select Table '.labelText$'
1647                         .line$ = Get value... 'findLabel.row' Text
1648                         endif
1649                         # Expand variables, eg, 'praatVersion$'
1650                         call expand_praat_variables '.line$'
1651                         .line$ = expand_praat_variables.text$
1652                         .textWidth = demo Text width (wc)... '.line$'
1653                         if .textWidth > .maxWidth
1654                                 .maxWidth = .textWidth
1655                         endif
1656                 endfor
1657         endfor
1658         if .dx > 1.2 * .maxWidth
1659                 .widthCanvas =  1.2 * .maxWidth * .numCols + 4
1660                 .xleft = 50 - .widthCanvas / 2
1661                 .xright = 50 + .widthCanvas / 2
1662                 .dx = (.widthCanvas - 4) / (.numCols)
1663         else
1664                 .maxWidth = .dx - 1
1665         endif
1666         
1667         # Calculate length from number of lines.
1668         .dy = .lineHeight + 0.5
1669         .midY = .yhigh - (.yhigh - .ylow)/2
1670         .yhigh = .midY + (.numLines+2) * .dy / 2
1671         .ylow = .yhigh - (.numLines+2) * .dy
1672         .textleft = .xleft + 2
1673         
1674         demo Line width... 8
1675         demo Colour... 'sgc2.popUp_bordercolor$'
1676         demo Paint rectangle... 'sgc2.popUp_backgroundcolor$' '.xleft' '.xright' '.ylow' '.yhigh'
1677         demo Draw rectangle... '.xleft' '.xright' '.ylow' '.yhigh'
1678         demo Line width... 'defaultLineWidth'
1679         demo Black
1680         .ytext = .yhigh - 2 - .dy
1681         # First the column names, then the items
1682         for .i from 0 to .numLines
1683                 .xtext = .textleft + .dx / 2
1684                 for .j to .numCols
1685                         select '.tabbedText'
1686                         .currentLabel$ = Get column label... '.j'
1687                         if .i > 0
1688                                 .line$ = Get value... '.i' '.currentLabel$'
1689                         else
1690                                 .line$ = .currentLabel$
1691                                 select Table '.labelText$'
1692                         call findLabel '.labelText$' '.line$'
1693                         select Table '.labelText$'
1694                         .line$ = Get value... 'findLabel.row' Text
1695                         endif
1696                         # Expand variables, eg, 'praatVersion$'
1697                         call expand_praat_variables '.line$'
1698                         .line$ = expand_praat_variables.text$
1699                         call adjustFontSizeOnWidth '.font$' '.fontSize' '.maxWidth' '.line$'
1700                         .currentFontSize = adjustFontSizeOnWidth.newFontSize
1702                         # Display text
1703                         demo Text special... '.xtext' Centre '.ytext' Bottom '.font$' '.currentFontSize' 0 '.line$'
1704                         .xtext += .dx
1705                 endfor
1706                 .ytext -= .dy
1707         endfor  
1708         demoShow()      
1709         call set_font_size 'defaultFontSize'
1710         select Table '.labelText$'
1711         Remove
1712         
1713         label ESCAPEwrite_tabbed_table
1714 endproc
1716 # Create a pop-up window with a given text
1717 procedure write_text_popup .font$ .size .text$
1718         .xleft = 10
1719         .xright = 90
1720         .ylow = 20
1721         .yhigh = 85
1722         .lineHeight = 3
1724         # Adapt size of button to length of text
1725         .maxWidth = (.xright - .xleft) - 4
1726         call adjustFontSizeOnWidth 'defaultFont$' '.size' '.maxWidth' '.text$'
1727         call adjustFontSizeOnHeight 'defaultFont$' '.size' '.lineHeight'
1728         .popupFontSize = min(adjustFontSizeOnWidth.newFontSize, adjustFontSizeOnHeight.newFontSize)
1729         if adjustFontSizeOnWidth.diff > 0
1730                 .xright += adjustFontSizeOnWidth.diff/4
1731                 .xleft -= 3*adjustFontSizeOnWidth.diff/4
1732         else
1733                 .xleft = ((.xright + .xleft) - adjustFontSizeOnWidth.textWidth)/2 - 2
1734                 .xright = ((.xright + .xleft) + adjustFontSizeOnWidth.textWidth)/2 + 2
1735         endif
1737         .numRows = 1
1738         # Calculate length from number of lines.
1739         .dy = .lineHeight
1740         .midY = .yhigh - (.yhigh - .ylow)/2
1741         .yhigh = .midY + (.numRows+1) * .dy / 2
1742         .ylow = .yhigh - (.numRows+1) * .dy
1743         .textleft = .xleft + 2
1744         .xmid = (.textleft + .xright - 2)/2
1745         
1746         demo Line width... 8
1747         demo Colour... 'sgc2.popUp_bordercolor$'
1748         demo Paint rectangle... 'sgc2.popUp_backgroundcolor$' '.xleft' '.xright' '.ylow' '.yhigh'
1749         demo Draw rectangle... '.xleft' '.xright' '.ylow' '.yhigh'
1750         demo Line width... 'defaultLineWidth'
1751         demo Black
1752         .ytext = .yhigh - 2 - .dy
1753         # Write text
1754         demo Text special... '.xmid' Centre '.ytext' Bottom '.font$' '.popupFontSize' 0 '.text$'
1756         demoShow()      
1757         demo 'defaultFont$'
1758         call set_font_size 'defaultFontSize'
1759 endproc
1761 # Write the background from a Text Table
1762 procedure draw_background .table$
1763         .xleft = 0
1764         .xright = 100
1765         .ylow = 0
1766         .yhigh = 100
1767         .lineHeight = 5
1768         .defaultColour$ = "{0.9,0.9,0.9}"
1769         .defaultAlign$ = "centre"
1771         # Get table with text and longest line
1772         call loadTable '.table$'
1773         .backgroundText = selected()
1774         .numLines = Get number of rows
1775         .backgroundFontSize = 28
1776         .referenceText$ = ""
1777         .maxlenght = 0
1778         .maxLine = 0
1779         .maxFontSize = 0
1780         .maxWidth = 0
1781         .textLines = 0
1782         for .l to .numLines
1783                 select '.backgroundText'
1784                 .currentText$ = Get value... '.l' text
1785                 # Expand variables, eg, 'praatVersion$'
1786                 call expand_praat_variables '.currentText$'
1787                 .currentText$ = expand_praat_variables.text$            
1788                 
1789                 .font$ = Get value... '.l' font
1790                 .fontSize = Get value... '.l' size
1791                 if .fontSize > .maxFontSize
1792                         .maxFontSize = .fontSize
1793                 endif
1794                 if not startsWith(.font$, "!")
1795                         call set_font_size '.fontSize'
1796                         .textWidth = demo Text width (wc)... '.currentText$'
1797                         if .textWidth > .maxWidth
1798                                 .maxWidth = .textWidth
1799                                 .backgroundFontSize = .fontSize
1800                                 .maxLine = .l
1801                         endif
1803                         .textLines += 1
1804                 endif
1805         endfor
1806         if .maxLine > 0
1807                 select '.backgroundText'
1808                 .referenceText$ = Get value... '.maxLine' text
1809                 .maxLineFont$ = Get value... '.maxLine' font
1810                 .backgroundFontSize = Get value... '.maxLine' size
1811                 .backgroundFontColour$ = Get value... '.maxLine' colour
1812                 call set_font_size '.maxFontSize'
1813         else
1814                 .maxFontSize = .backgroundFontSize
1815         endif
1816         
1817         # Adapt size of button to length of text
1818         .maxWidth = (.xright - .xleft) - 4
1819         .origFontSize = .backgroundFontSize
1820         call adjustFontSizeOnWidth 'defaultFont$' '.backgroundFontSize' '.maxWidth' '.referenceText$'
1821         .fontSizeFactor = adjustFontSizeOnWidth.newFontSize / .backgroundFontSize
1822         .backgroundFontSize = adjustFontSizeOnWidth.newFontSize
1823         call set_font_size '.backgroundFontSize'
1824         
1825         call adjustFontSizeOnHeight 'defaultFont$' '.backgroundFontSize' '.lineHeight'
1826         .lineHeight /= adjustFontSizeOnHeight.newFontSize / .backgroundFontSize
1827         if adjustFontSizeOnHeight.newFontSize >= .origFontSize and (.textLines+1) * .lineHeight > (.yhigh - .ylow - 4)
1828                 .lineHeight = (.yhigh - .ylow - 4)/(.textLines + 1)
1829                 call adjustFontSizeOnHeight 'defaultFont$' '.maxFontSize' '.lineHeight'
1830                 .fontSizeFactor = adjustFontSizeOnHeight.newFontSize / .backgroundFontSize
1831         endif
1833         .numRows = Get number of rows
1834         # Calculate length from number of lines.
1835         .dy = .lineHeight
1836         .midY = .yhigh - (.yhigh - .ylow)/2
1837         .yhigh = .midY + (.textLines+1) * .dy / 2
1838         .ylow = .yhigh - (.textLines+1) * .dy
1839         .textleft = .xleft + 2
1840         .textright = .xright - 2
1841         .textmid = (.xright - .xleft)/2
1842         
1843         demo Black
1844         .ytext = .yhigh - 2 - .dy
1845         for .i to .numRows
1846                 select '.backgroundText'
1847                 .font$ = Get value... '.i' font
1848                 .fontSize = Get value... '.i' size
1849                 .fontColour$ = Get value... '.i' colour
1850                 .fontColour$ = replace_regex$(.fontColour$, "^[\- ]$", ".defaultColour$", 1)
1851                 .fontAlign$ = Get value... '.i' align
1852                 .fontAlign$ = replace_regex$(.fontAlign$, "^[\- ]$", ".defaultAlign$", 1)
1853                 .line$ = Get value... '.i' text
1854                 # Expand variables, eg, 'praatVersion$'
1855                 call expand_praat_variables '.line$'
1856                 .line$ = expand_praat_variables.text$
1857                                 
1858                  # Scale font
1859                  .fontSize = floor(.fontSize*.fontSizeFactor)
1860                 if not startsWith(.font$, "!")
1861                         .font$ = extractWord$(.font$, "")
1863                         if .fontAlign$ = "centre"
1864                                 .xtext = .textmid
1865                         elsif .fontAlign$ = "right"
1866                                 .xtext = .textright
1867                         else
1868                                 .xtext = .textleft
1869                         endif
1870                         if .fontSize < 4
1871                                 .fontSize = 4
1872                         endif
1873                         # Clean up text
1874                         demo Colour... '.fontColour$'
1875                         demo Text special... '.xtext' '.fontAlign$' '.ytext' Bottom '.font$' '.fontSize' 0 '.line$'
1876                         .ytext -= .dy
1877                 elsif .font$ = "!demo command"
1878                         demo Colour... '.fontColour$'
1879                         .line$ = replace_regex$(.line$, "\{FONTSIZE\$\}", "'.fontSize'", 0)
1880                         .line$ = replace_regex$(.line$, "\{XTEXT\$\}", "'.xtext'", 0)
1881                         .line$ = replace_regex$(.line$, "\{YTEXT\$\}", "'.ytext'", 0)
1882                         .line$ = replace_regex$(.line$, "\{DY\$\}", "'.dy'", 0)
1883                         .line$ = replace_regex$(.line$, "\{[^\}]*\}", "", 0)
1884                         while index(.line$, "[[")
1885                                 .nextBracketOpen = index(.line$, "[[")
1886                                 .nextBracketOpen += 2
1887                                 .nextBracketClose = index(.line$, "]]")
1888                                 .bracketLength = .nextBracketClose - .nextBracketOpen
1889                                 .result$ = ""
1890                                 if .bracketLength > 0
1891                                         .expression$ = mid$(.line$, .nextBracketOpen, .bracketLength)
1892                                         .expression$ = replace_regex$(.expression$, "\s", "", 0)
1893                                         if length(.expression$) > 0
1894                                                 # Test expression for security, only allow explicitely defined functions
1895                                                 .allowedStrings$ = "e|pi|not|and|or|div|mod|abs|round|floor|ceiling"
1896                                                 .allowedStrings$ = .allowedStrings$ + "|sqrt|min|max|imin|imax|sin|cos|tan|arcsin|arccos|arctan|arctan2|sinc|sincpi"
1897                                                 .allowedStrings$ = .allowedStrings$ + "|exp|ln|log10|log2|sinh|cosh|tanh|arcsinh|arccosh|arctanh"
1898                                                 .allowedStrings$ = .allowedStrings$ + "|sigmoid|invSigmoid|erf|erfc|randomUniform|randomInteger|randomGauss|randomPoisson"
1899                                                 .allowedStrings$ = .allowedStrings$ + "|lnGamma|gaussP|gaussQ|invGaussQ|chiSquareP|chiSquareQ"
1900                                                 .allowedStrings$ = .allowedStrings$ + "|invChiSquareP|invChiSquareQ|studentP|studentQ|invStudentP|invStudentQ"
1901                                                 .allowedStrings$ = .allowedStrings$ + "|beta|besselI|besselK"
1902                                                 .testExpression$ = replace_regex$(.expression$, "(^|\W)('.allowedStrings$')(?=$|\W)", "\1\3", 0)
1903                                                 .testExpression$ = replace_regex$(.testExpression$, "[0-9\.,\-+/*^()<>= ]", "", 0)
1904                                                 if .testExpression$ = ""
1905                                                         .calc = '.expression$'
1906                                                         .result$ = "'.calc'"
1907                                                 endif
1908                                         endif
1909                                 endif
1910                                 
1911                                 # Replace expression by result
1912                                 .lastLeft = .nextBracketOpen - 3
1913                                 .newLine$ = left$(.line$, .lastLeft)  
1914                                 .newLine$ =  .newLine$ + .result$
1915                                 .numCopy = length(.line$) - .nextBracketClose - 1
1916                                 .newLine$ =  .newLine$ + right$(.line$, .numCopy)
1917                                 .line$ = .newLine$
1918                         endwhile
1919                         demo '.line$'
1920                 endif
1921         endfor  
1922         demo Black
1923         demoShow()      
1924         call set_font_size 'defaultFontSize'
1925         
1926         select '.backgroundText'
1927         Remove
1928 endproc
1930 procedure convert_praat_to_utf8 .text$
1931         .text$ = replace_regex$(.text$, "\\a""", "\xc3\xa4", 0)
1932         .text$ = replace_regex$(.text$, "\\A""", "\xc3\x84", 0)
1933         .text$ = replace_regex$(.text$, "\\o""", "\xc3\xb6", 0)
1934         .text$ = replace_regex$(.text$, "\\O""", "\xc3\x96", 0)
1935         .text$ = replace_regex$(.text$, "\\u""", "\xc3\xbc", 0)
1936         .text$ = replace_regex$(.text$, "\\U""", "\xc3\x9c", 0)
1937         .text$ = replace_regex$(.text$, "\\i""", "\xc3\xaf", 0)
1938         .text$ = replace_regex$(.text$, "\\I""", "\xc3\x8f", 0)
1939         .text$ = replace_regex$(.text$, "\\e""", "\xc3\xab", 0)
1940         .text$ = replace_regex$(.text$, "\\E""", "\xc3\x8b", 0)
1941         .text$ = replace_regex$(.text$, "\\y""", "\xc3\xbf", 0)
1942         .text$ = replace_regex$(.text$, "\\Y""", "\xc3\x9f", 0)
1943         .text$ = replace_regex$(.text$, "\\e'", "\xc3\xa9", 0)
1944         .text$ = replace_regex$(.text$, "\\E'", "\xc3\x89", 0)
1945         .text$ = replace_regex$(.text$, "\\ss", "\xc3\x9f", 0)
1946         .text$ = replace_regex$(.text$, "\\bu", "\xc3\x95", 0)
1947 endproc
1949 procedure convert_praat_to_latin1 .text$
1950         .text$ = replace_regex$(.text$, "\\a""", "\xe4", 0)
1951         .text$ = replace_regex$(.text$, "\\A""", "\xc4", 0)
1952         .text$ = replace_regex$(.text$, "\\o""", "\xf6", 0)
1953         .text$ = replace_regex$(.text$, "\\O""", "\xd6", 0)
1954         .text$ = replace_regex$(.text$, "\\u""", "\xfc", 0)
1955         .text$ = replace_regex$(.text$, "\\U""", "\xdc", 0)
1956         .text$ = replace_regex$(.text$, "\\i""", "\xef", 0)
1957         .text$ = replace_regex$(.text$, "\\I""", "\xcf", 0)
1958         .text$ = replace_regex$(.text$, "\\e""", "\xeb", 0)
1959         .text$ = replace_regex$(.text$, "\\E""", "\xcb", 0)
1960         .text$ = replace_regex$(.text$, "\\y""", "\xff", 0)
1961         .text$ = replace_regex$(.text$, "\\Y""", "\x9f", 0)
1962         .text$ = replace_regex$(.text$, "\\e'", "\xe9", 0)
1963         .text$ = replace_regex$(.text$, "\\E'", "\xc9", 0)
1964         .text$ = replace_regex$(.text$, "\\ss", "\xdf", 0)
1965         .text$ = replace_regex$(.text$, "\\bu", "\x95", 0)
1966 endproc
1968 # Expand 'variable$' into the value of variable$.
1969 # Eg, 'praatVersion$' becomes 5.1.45 or whatever is the current version
1970 # Single quotes can be protected by \'
1971 procedure expand_praat_variables .text$
1972         if index(.text$, "'")
1973                 .tempText$ = replace_regex$(.text$, "(^|[^\\])'([\w\$\.]+)'", "\1""+\2+""", 0)
1974                 .tempText$ = replace_regex$(.tempText$, "[\\]'", "'", 0)
1975                 .tempText$ = """"+.tempText$+""""
1976                 # Check whether all the variables actually exist. Ignore any variable that does not exist
1977                 .checkVars$ = .tempText$
1978                 while length(.checkVars$) > 0 and index(.checkVars$, "+")
1979                         .start = index(.checkVars$, "+")
1980                         .checkVars$ = right$(.checkVars$, length(.checkVars$) - .start)
1981                         .end = index(.checkVars$, "+")
1982                         if .end
1983                                 .variable$ = left$(.checkVars$, .end - 1)
1984                                 if not variableExists(.variable$)
1985                                         .tempText$ = replace$(.tempText$, """+'.variable$'+""", "'"+.variable$+"'", 0)
1986                                 endif
1987                                 .checkVars$ = right$(.checkVars$, length(.checkVars$) - .end)
1988                         else
1989                                 .checkVars$ = ""
1990                         endif
1991                 endwhile
1992                 .text$ = '.tempText$'
1993         endif
1994 endproc
1996 # Get a time stamp in normalized format
1997 procedure getTimeStamp
1998         .currentDateTime$ = date$()
1999         .string$ = replace_regex$(.currentDateTime$, "[A-Z][a-z]+\s+([A-Z][a-z]+)\s+(\d+)\s+(\d+)\W(\d+)\W(\d+)\s+(\d+)$", "\6-\1-\2T\3-\4-\5", 0)
2000 endproc
2002 # A table error, can be insiduously caused by an outdate preferences file!
2003 procedure emergency_table_exit .message$
2004         # If you come here as a user, your preferences file is borked
2005         if preferencesAppFile$ <> "" and fileReadable(preferencesAppFile$)
2006                 deleteFile(preferencesAppFile$)
2007         endif
2008         # Put out message
2009         call get_feedback_text 'config.language$' Cancel
2010         call convert_praat_to_latin1 'get_feedback_text.text$'
2011         .cancelText$ = convert_praat_to_latin1.text$
2012         beginPause: "Fatal Error"
2013         comment: .message$
2014         endPause: .cancelText$, 1
2015         exit '.message$'
2016 endproc
2018 # Remove previous files from system
2019 procedure clean_up_sound
2020         if recordedSound$ = ""
2021                 sgc.recordedSound = 0
2022         endif
2023     if sgc.recordedSound > 0
2024         select sgc.recordedSound
2025         Remove
2026         recordedSound$ = ""
2027         sgc.recordedSound = 0
2028     endif
2029     if te.recordedPitch > 0
2030         select te.recordedPitch
2031         Remove
2032                 te.recordedPitch = 0
2033     endif
2034         if sgc.alignedTextGrid > 0
2035                 select sgc.alignedTextGrid
2036                 Remove
2037                 sgc.alignedTextGrid = -1
2038         endif
2039 endproc
2041 # Safely read a table
2042 procedure readTable .filename$
2043         .tableID = -1
2044         if .filename$ <> "" and fileReadable(.filename$) and index_regex(.filename$, "(?i\.(tsv|table|csv))$") > 0
2045                 if index_regex(.filename$, "(?i\.(csv))$") > 0
2046                         .tableID = Read Table from comma-separated file: .filename$
2047                 elsif index_regex(.filename$, "(?i\.(tsv))$") > 0
2048                         .tableID = nocheck Read Table from tab-separated file: .filename$
2049                 else
2050                         .tableID = nocheck Read from file... '.filename$'
2051                 endif
2052                 if .tableID = undefined or .tableID <= 0
2053                         .tableID = -1
2054                 else
2055                         .fullName$ = selected$ ()
2056                         .type$ = extractWord$(.fullName$, "")
2057                         if .type$ <> "Table"
2058                                 Remove
2059                                 .tableID = -1
2060                         endif
2061                 endif
2062         endif
2063 endproc
2065 # Read feedback table and get keyed text
2066 procedure get_feedback_text .language$ .key$
2067         if not endsWith(feedbackTableName$, "_'.language$'")
2068                 if feedbackTableName$ <> ""
2069                         select Table 'feedbackTableName$'
2070                         Remove
2071                 endif
2072                 call testLoadTable 'feedbackTablePrefix$'_'.language$'
2073                 if testLoadTable.table > 0
2074                         call loadTable 'feedbackTablePrefix$'_'.language$'
2075                 else
2076                         call loadTable 'feedbackTablePrefix$'_EN
2077                 endif
2078                 feedbackTableName$ = selected$("Table")
2079         endif
2080         call findKey 'feedbackTableName$' '.key$'
2081         .row = findKey.row
2082         select Table 'feedbackTableName$'
2083         .text$ = Get value... '.row' Text
2084         # Expand variables, eg, 'praatVersion$'
2085         call expand_praat_variables '.text$'
2086         .text$ = expand_praat_variables.text$   
2087 endproc
2090 # Read evaluation table and get keyed text. Only praat converted Text
2091 procedure get_evaluation_text .language$ .key$
2092         if not endsWith(evaluationTableName$, "_'.language$'")
2093                 if evaluationTableName$ <> ""
2094                         select Table 'evaluationTableName$'
2095                         Remove
2096                 endif
2097                 call testLoadTable 'evaluationTablePrefix$'_'.language$'
2098                 if testLoadTable.table > 0
2099                         call loadTable 'evaluationTablePrefix$'_'.language$'
2100                 else
2101                         call loadTable 'evaluationTablePrefix$'_EN
2102                 endif
2103                 evaluationTableName$ = selected$("Table")
2104         endif
2105         call findLabel 'evaluationTableName$' '.key$'
2106         .row = findLabel.row
2107         select Table 'evaluationTableName$'
2108         .text$ = Get value... '.row' Text
2109         # Expand variables, eg, 'praatVersion$'
2110         call expand_praat_variables '.text$'
2111         .rawtext$ = expand_praat_variables.text$
2112         call convert_praat_to_latin1 '.rawtext$'
2113         .text$ = convert_praat_to_latin1.text$
2114 endproc
2116 # Read all the relevant evaluation labels and put them in "eval.<label>$" variables
2117 procedure get_evaluation_table_labels .language$
2118         call get_evaluation_text '.language$' Performance
2119         eval.performance$ = get_evaluation_text.text$
2120         call get_evaluation_text '.language$' Pinyin
2121         eval.pinyin$ = get_evaluation_text.text$
2122         call get_evaluation_text '.language$' Correct
2123         eval.correct$ = get_evaluation_text.text$
2124         call get_evaluation_text '.language$' Wrong
2125         eval.wrong$ = get_evaluation_text.text$
2126         call get_evaluation_text '.language$' Total
2127         eval.total$ = get_evaluation_text.text$
2128         call get_evaluation_text '.language$' High
2129         eval.high$ = get_evaluation_text.text$
2130         call get_evaluation_text '.language$' Low
2131         eval.low$ = get_evaluation_text.text$
2132         call get_evaluation_text '.language$' Wide
2133         eval.wide$ = get_evaluation_text.text$
2134         call get_evaluation_text '.language$' Narrow
2135         eval.narrow$ = get_evaluation_text.text$
2136         call get_evaluation_text '.language$' Unknown
2137         eval.unknown$ = get_evaluation_text.text$
2138         call get_evaluation_text '.language$' Commented
2139         eval.commented$ = get_evaluation_text.text$
2140         call get_evaluation_text '.language$' Level
2141         eval.level$ = get_evaluation_text.text$
2142         call get_evaluation_text '.language$' Time
2143         eval.time$ = get_evaluation_text.text$
2144         call get_evaluation_text '.language$' Wordlist
2145         eval.wordlist$ = get_evaluation_text.text$
2146         
2147 endproc
2149 # log activity
2150 procedure saveLogOfActivity .command$
2151         # Do not log in binary!
2152         if build_SHA$ = "-"
2153                 createDirectory(preferencesLogDir$)
2154                 appendFileLine: "'preferencesLogDir$'/'logtimeStamp$'.log", .command$
2155         endif
2156 endproc
2158 # Replay a log file with commands sgc.replaySleep inserts a pause
2159 procedure replaySGC2LogFunction
2160         if not variableExists("sgc.replaySleep")
2161                 sgc.replaySleep = -1
2162         endif
2163         # Do not replay in binary!
2164         if build_SHA$ = "-"
2165                 # Ask for the input file
2166                 .filename$ = chooseReadFile$ ("Select file to replay")
2167                 if .filename$ <> "" and fileReadable(.filename$)
2168                         .replayFile = Read Strings from raw text file: .filename$
2169                         if .replayFile <> undefined
2170                                 # Pre-pause
2171                                 if sgc.replaySleep > 0
2172                                         call basic_sound_recording 'samplingFrequency' 'sgc.replaySleep'
2173                                         Remove
2174                                 endif
2175                                 select .replayFile
2176                                 .numStrings = Get number of strings
2177                                 for .l to .numStrings
2178                                         select .replayFile
2179                                         .line$ = Get string: .l
2180                                         if index_regex(.line$, "process(MainPage|Config)(Help|Config|Return|Quit)")
2181                                                 if index(.line$, "processMainPageConfig")
2182                                                         .line$ = "call Draw_config_page"
2183                                                 elsif index_regex(.line$, "processConfigReturn")
2184                                                         .line$ = "call init_window"
2185                                                 elsif index_regex(.line$, "processMainPageHelp")
2186                                                         .line$ = "call init_window"
2187                                                 elsif index_regex(.line$, "processConfigHelp")
2188                                                         .line$ = "call Draw_config_page"
2189                                                 else
2190                                                         .line$ = "# " + .line$
2191                                                 endif
2192                                         endif
2193                                         if index_regex(.line$, "[a-zA-Z]") > 0 and index_regex(.line$, "\s*#") <= 0
2194                                                 # Execute
2195                                                 '.line$'
2196                                                 
2197                                                 # Pause
2198                                                 if sgc.replaySleep > 0
2199                                                         call basic_sound_recording 'samplingFrequency' 'sgc.replaySleep'
2200                                                         Remove
2201                                                 endif
2202                                         endif
2203                                 endfor
2204                         endif
2205                 endif
2206         endif
2207 endproc