Week 12: Procedures

Dr. Fernanda Barrientos

February 07, 2023

1 Intro

In previous weeks we have seen how for-loops can help us perform operations on a series of Objects, variables, etc.: they are one of the most important tools for scripting.

However, there is a second tool that is even more powerful than loops. They are called procedures, and have the virtue of (a) in some cases, make the scripts shorter; and (b) add a boost to the power of your script by being able to stack variables for one single process.

2 A minimal example

Basically, a procedure is a set of actions that can be defined somewhere in the script and then be "called" by a variable or set of them. Let's create an example with string variables (yes, procedures can be run over string and numeric variables). Let's say that we have two long words (hippopotamus and dodecahedron), and we want to count the number of letters in them. We want to use the length (mystring$) function to calculate the length, of each word, and then we want to print the result to the info window.

@myProc: "hippopotamus"
@myProc: "dodecahedron"

procedure myProc: .longword$
.number= length (.longword$)
appendInfoLine: .longword$, tab$, .number
endproc

What is this? Let's see:

3 More examples

3.1 Get pitch values at different times

Let's pretend that we have a vowel from a speaker of Mandarin and we would like to get pitch values throughout the vowel at three points: start, mid, and end. In this case, we have just visually inspected them and know that these times are 0.17, 0.3, and 0.58. We want to print the values to the Info window.

appendInfoLine: "Time", tab$, "Pitch"
#upon visual inspection, we enter start, mid, and end times of the vowel. We write down the following:
@getValue: 0.17
@getValue: 0.3
@getValue: 0.58

Where getValue is the name of the procedure, followed by the times from which we want to get the pitch values. Remember that the @ is the way we say "Praat, call the procedure on these values". Now we create the procedure itself, which can go anywhere in your script.

procedure getValue: .time
selectObject: "Sound ma2"
p=To Pitch: 0, 75, 600
ps=Smooth: 10
.val= Get value at time: .time, "Hertz", "linear"
appendInfoLine: .time, tab$, .val
endproc

Line 6 defines the procedure as getValue, which is the name we used in lines 3-5 when calling it. Then we have a assigned an internal name to the variables above for future reference: we called it .time . Then we select the Sound and create the Pitch Object and smoothen it (lines 7-9). Afterwards we create a new variable called .val, which stores the pitch value at the time stated on line 3 of this script. Finally, we print the information to the Info window. The last line closes the procedure. After this, the script will move on to the following @getValue (line 4), repeat, then apply to the next @getValue (line 5). You can call the same procedure as many times as you like throughout your script.

You can add as many actions inside your procedure. Here we added the creation of a Formant object (line 7), in case we wanted to get the F1 values at the same times stated above:

procedure getValue: .time
selectObject: "Sound ma2"
p=To Pitch: 0, 75, 600
ps=Smooth: 10
.val= Get value at time: .time, "Hertz", "linear"
selectObject: "Sound ma2"
form= To Formant (burg): 0, 5, 5500, 0.025, 50
.meanf1= Get value at time: 1, .time, "hertz", "linear"
appendInfoLine: .time, tab$, .val, tab$, .meanf1
endproc

3.2 Create several continua

3.2.1 Without procedure

Let's assume that we are creating a continuum between /i/ and /u/, as we did as a homework last week. We will need to create a few variables, and then feed them to a KlattGrid:

#We define f1, the f2 for the frontmost vowel, and the f2 for the backmost one
f1=300
f2_front=2800
f2_back=1200

After opening the for-loop (line 5), we create a KlattGrid named "vowel", which will be 0.05 s long, will have a maximum of 6 formants, etc. (line 6). We will add two pitch points in order to give a more natural, descending pitch contour (lines 7-8); then we add an amplitude point (line 9), and one formant frequency point and bandwidth point for each formant (lines 10-13). Finally, we synthesize the sounds (line 14) and save them in a folder called continuum on our Desktop (line 15). Line 16 closes the for-loop.

for i from 1 to 5
    Create KlattGrid: "vowel", 0, 0.5, 6, 1, 1, 6, 1, 1, 1
    Add pitch point: 0.05, 180
    Add pitch point: 0.45, 170
    Add voicing amplitude point: 0.25, 90
    Add oral formant frequency point: 1, 0.25, f1
    Add oral formant bandwidth point: 1, 0.25, 50
    Add oral formant frequency point: 2, 0.25, f2_front -(((f2_front - f2_back)/4)*i-1)
    Add oral formant bandwidth point: 2, 0.25, 50
    To Sound
    Save as WAV file: "/home/fernanda/Desktop/continuum/" + "vowel_"+string$(i)+".wav"
endfor

However, this is not a good approach, as we will need to repeat the same for-loop for the other continuum. A procedure will be useful in this case beacuse it would allow us to create one for-loop for both continua.

3.2.1 With procedure

Let's look at the following script:

@createCont: 300, "high", 2800, 1100
@createCont: 800, "low", 1800, 1200

What did we just do? Here we are going to call a procedure named createCont, which will apply to a series of variables. The first instance has the parameters for creating a continuum between high vowels /i/ and /u/. In order to create this continuum, we need the values for the F1 (300 Hz for both vowels), and then the F2 values for /i/ (the frontest vowel: 2800 Hz) and /u/ (the backest vowel: 1100 Hz). The procedure will calculate the steps.

The second instance contains the parameters for creating the continuum between low vowels /æ/ and ɑ/. Note that we have listed the parameters in the same order as above; this is crucial.

Now let's move on to the procedure chunk:

procedure createCont: .f1, .height$, .f2_front, .f2_back
for i from 1 to 5
    Create KlattGrid: .height$+string$(i), 0, 0.5, 6, 1, 1, 6, 1, 1, 1
    Add pitch point: 0.05, 180
    Add pitch point: 0.45, 170
    Add voicing amplitude point: 0.25, 90
    Add oral formant frequency point: 1, 0.25, .f1
    Add oral formant bandwidth point: 1, 0.25, 50
    Add oral formant frequency point: 2, 0.25, .f2_front -(((.f2_front - .f2_back)/4)*i-1)
    Add oral formant bandwidth point: 2, 0.25, 50
    To Sound
    Save as WAV file: "/home/fernanda/Desktop/continua/" + .height$+string$(i)+".wav"
    endfor
endproc

Line 3 gives names to each one of the parameters stated in the lines above, so that the procedure can use them. So the first variable was called .f1, the second one was called .height$, the third one is .f2_front, and the fourth one is .f2_back. Then, the procedure refers to these variables and operates on them. So we turned the for-loop in section 3.2.1. into a procedure that can be applied to different sets of values, with very little changes.

4 Homework

Download the following sound from here (you can also find it on ILIAS). We will create four Sound objects, each one with different Mandarin tones. For this: