Session 11
Simple Input Forms
Input Forms
Do you remember the concatenation script from the session on object selection? It combines two sounds with a silence interval of 1 second in between:
first_sound = selected ("Sound", 1)
second_sound = selected ("Sound", 2)
selectObject: second_sound
samp_freq = Get sampling frequency
sil = Create Sound from formula: "silence", 1, 0, 1, samp_freq, "0"
selectObject: second_sound
copy = Copy: "tmp"
plusObject: sil
plusObject: first_sound
Concatenate
Play
removeObject: copy, sil
If you want the script to be more flexible regarding silence duration, the easiest solution would be to declare silence duration at the beginning of the script (we already discussed this principle in other contexts):
sil_dur = 1
first_sound = selected ("Sound", 1)
second_sound = selected ("Sound", 2)
selectObject: second_sound
samp_freq = Get sampling frequency
sil = Create Sound from formula: "silence", 1, 0, sil_dur, samp_freq, "0"
selectObject: second_sound
copy = Copy: "tmp"
plusObject: sil
plusObject: first_sound
Concatenate
Play
removeObject: copy, sil
With the declaration in place, changing silence duration is simply a matter of changing the assigned value. But in fact, this is only simple for the script author. If the script is distributed, scripting affine people can be instructed how to adapt the script, but naïve Praat users might be out of their depth when asked to change a variable assignment.
And there's another scenario where script customization by editing declarations inside the script is unfeasible: If you use the script often enough you may want it integrated in the Praat GUI, so it's always handy when you work with sounds. For instance, you can create a permanent menu item in the
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1
endform
As soon as this script is launched the following form window pops up:
Let's have a closer look at the code and compare it to the resulting window. The definition of a form always starts with the keyword form
. As you can see in the screen shot, the rest of the line becomes the title of the window containing the form. It has to be a literal string (without double quotes, for a change) and it is obligatory. endform
concludes the form definition, everything in between defines the content of the form (indentation is optional but recommended).
The form
-construct is a relict that obeys to the outdated rules of the old Praat scripting syntax. These rules weren't yet aware of the benefits of colons, commas, and double-quoted strings. Therefore, throughout form definitions, you have to dispense with colons after commands, commas between arguments, and quotes around strings.
Input forms are
composed of fields
A form definition consists of a sequence of field definitions, one field definition per line. Fields are the boxes (or, as you'll see in a minute, buttons or menus) where the user input is conceived. Our form above contains a special 'field', one that doesn't conceive any input: a comment. This is followed by a input box for the silence duration. Every field definition starts with the declaration of the field's type. The first field in the example is of the type comment, which is just some text to communicate with the user. The comment itself—a literal string without double quotes—follows the type declaration separated by a space.
'Genuine' fields, i.e. fields that conceive some input, need a variable to store the input when the user clicks OK. So with all fields except comments, the type declaration is followed (separated by a space) by a variable name, more accurately: by a particularly structured representation of a variable name. Our second field is a genuine input field of the type positive, i.e. the user is only allowed to enter a positive real number (to avoid the input of negative durations). Silence_duration_(s) represents a variable and is discussed in a moment. As you can see in the screen shot, it appears in front of the input box with the underscores replaced by spaces and a colon added. The last parameter (1) is the (optional) standard value that appears in the input box when the form is displayed (or when the Standards button is clicked).
As soon as the user clicks OK, the window is closed and the input is stored into variables. These variables can be subsequently used in the script to process the input. In our example, the variable to store the input is represented as Silence_duration_(s), but the 'real' name of the variable, i.e. the name that has to be used in the script after the form was confirmed, is silence_duration. Why's that?
Variables in forms
Variables in forms have two jobs: (1) They have to store the input (clicking OK triggers assignment of values to variables), which is a genuine variable job, and (2) they serve as title of their input field, elucidating the meaning of the input field. The second job is much about appearance, which isn't very variable-like. This is the reason why Praat permits some tricks with form variables, inventing a representation level which is parsed in two ways: The representation—Silence_duration_(s)—is first parsed for appearance, replacing underscores with spaces and adding a colon at the end. Second, it's parsed for script usage, aiming at syntactically correct variable names. This means:
- most importantly, replacing an initial upper case letter with a lower case letter:
Silence_duration_(s) ⇒ silence_duration_(s) - stripping possible _(…)-stuff off the end:
silence_duration_(s) ⇒ silence_duration
Thanks to this mechanism, we're free to formulate nice looking field titles, with capital letters and hints about the unit of the input (s for seconds in the example), while at the same time, we have perfectly suitable variable names available in the script. (Be careful: The chopping of _(…)-stuff works reliably only if you avoid spaces between the brackets.) Now, let's integrate the form in the script:
# form definition
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1
endform
# processed after the form is closed:
first_sound = selected ("Sound", 1)
second_sound = selected ("Sound", 2)
selectObject: second_sound
samp_freq = Get sampling frequency
sil = Create Sound from formula: "silence", 1, 0, silence_duration, samp_freq, "0"
selectObject: second_sound
copy = Copy: "tmp"
plusObject: sil
plusObject: first_sound
Concatenate
Play
removeObject: copy, sil
When the script is launched, first thing the input form pops up and asks for the silence duration. After OK is clicked, the desired silence duration is available in the variable silence_duration and can be used for sound creation.
Another trick to beautify input forms: Standard values may be annotated. Let's try and suggest to the user that we think 1 second is a pretty long pause (I'am sure there are more useful applications of this trick…):
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1 (= pretty long)
endform
The result:
If the user clicks OK the comment (which has to be enclosed in brackets) is ignored, and only the positive real number 1 is assigned to its variable.
Input forms are
always processed first
Input forms (and procedures) are exceptions to the strict procedural execution principle. To put it simply, procedural execution means the Praat scripting engine starts interpreting and executing in the first line, then proceeds line per line until either the first error occurs or the last line is reached. Input forms are different because they are always processed first, independent where the code block is placed in the script. Hence, the script below behaves exactly like the one above:
# processed after the form is closed:
first_sound = selected ("Sound", 1)
second_sound = selected ("Sound", 2)
selectObject: second_sound
samp_freq = Get sampling frequency
sil = Create Sound from formula: "silence", 1, 0, silence_duration, samp_freq, "0"
selectObject: second_sound
copy = Copy: "tmp"
plusObject: sil
plusObject: first_sound
Concatenate
Play
removeObject: copy, sil
# form definition
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1
endform
This is nonsense, needless to say, but it's formally correct and illustrative of what I said before. Intuitively, you would probably place a form like that at the beginning of the script, which is best practice because it's the most logical position.
The non-procedural execution of forms has two important consequences:
- You can't have two forms in one script. Forms are processed and executed before everything else and it's impossible to process more than one thing before everything else. (To be pedantic: You can have two forms in one script, but the second one is just ignored.)
- You can't replace literal values with variables in forms. Because forms are processed before everything else, any variable assignment happens afterwards, so the form isn't aware of variables let alone their assigned values.
The last point prohibits for example, to use queried values as standard values in forms. Suppose you want to ask the user for a pitch ceiling and you want to provide the maximum pitch of the signal under consideration as standard value in the form, to give the user some orientation. To achieve this, you could try something like that:
max = Get maximum: 0, 0, "Hertz", "Parabolic")
form Specify pitch ceiling
positive Pitch_ceiling_(Hz) max
endform
Despite its position after the variable assignment, the form is processed first. Thus, as far as the form is concerned, max is just some string, so the literal string max is placed in the input box as standard value. If the user replaces it with a number, everything is fine. If the user doesn't replace it and clicks OK, Praat grumbles, because the string max is not a positive real number.
If you want to use variables in forms or if you need forms that acknowledge the procedural execution principle or if you prefer form definitions where literal strings are neatly enclosed in double quotes, you can look forward to Flexible Input Forms.
Strings in forms
Back to more practical issues. So far, we've only asked for a positive real number. As you'll see in the next section, Praat provides a whole bunch of other field types to inquire other kinds of numbers. And there are three field types that accept strings. We'll use one of them to ask the user for a name for the concatenated sound, as an alternative to the Praat standard name chain. To prevent names with spaces we use the field type word, which accepts only strings devoid of spaces. (To be accurate: word accepts any string (i.e. it won't issue an error message if the user puts in a string with spaces) but it assigns only the initial non-space sequence to its variable, ignoring everything after the first space.) Naturally, the input string has to be assigned to a string variable, but the characteristic final $-symbol—this is the last trick, which is more than a trick, it's an obligation—must be omitted in the field definition! Here's the correct code block:
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1
comment Specify a name for the concatenated sound.
word Concatenated_name chain
endform
This field definition wouldn't work (aside from being ugly):
word Concatenated_name$ chain
But later in the script, you must use concatenated_name$, with initial lower case letter and final $-symbol! The renaming is implemented with one simple statement near the end, before playing the concatenated sound:
form Concatenate with silence
comment Specify duration of silence between sounds.
positive Silence_duration_(s) 1
comment Specify a name for the concatenated sound.
word Concatenated_name chain
endform
first_sound = selected ("Sound", 1)
second_sound = selected ("Sound", 2)
selectObject: second_sound
samp_freq = Get sampling frequency
sil = Create Sound from formula: "silence", 1, 0, silence_duration, samp_freq, "0"
selectObject: second_sound
copy = Copy: "tmp"
plusObject: sil
plusObject: first_sound
Concatenate
# rename selected object
Rename: concatenated_name$
Play
removeObject: copy, sil
Next: Field Types