1 Creating a Praat script

A huge advantage to the Praat software is its ability to be scripted. By creating a Praat script, you have the ability to automate your task at hand.

In order to create a script, you’ll need to open a new window in which to type your script. From the Praat menu, select ‘New Praat script’, as shown in Figure 1 below.

Figure 1. Opening a Praat Script

Figure 1. Opening a Praat Script

This command will open a window that looks the window shown in Figure 2.

Figure 2. A Blank Praat Script

Figure 2. A Blank Praat Script

Don’t forget: save often! Don’t forget that Praat has no AutoSave function. Make sure to save your script, and save it often. When you save your script, use a .praat extension (i.e. name it something like ‘test_script.praat’)

2 Measurements from TextGrids

Last time, we talked about what TextGrids are. Assuming you have one that is annotated with boundaries for phone and word, there are several measurements you might want to take that are based on these boundaries.

For now, we’ll focus on measurements you can get just using the TextGrids, but you can also use the TextGrids to ask Praat to measure an acoustic feature at a certain period in time that is retreived from the TextGrid.

Let’s see how to get the duration of one or more of these segments.

If you wanted to get the duration of a segment by hand, you’d select the segment and then use the command “Get selection length” from the Query menu to see how long the segment selected is. For example, in Figure 3, we are measuring the duration of the “EH” of “educate”.

Figure 3. Manual Retreival of Duration

Figure 3. Manual Retreival of Duration

You could also get the duration of a segment using a script. This script requires you to have already opened your TextGrid with Praat.

First, we’ll tell Praat where our tiers are and rename our TextGrid so that if you have to change your tier numbers or TextGrid name later, you only have to do it once at the top of your script. Then, Praat will get the label of the interval, find the start and end point of the interval (and thus, we can calculate duration). This example also introduces you to appendInfoLine which provides output by appending to the Praat info window.

There are two types of variables in Praat. A variable that contains a ‘string’ (a sequence of characters) is notated with a dollar sign after the variable (e.g. text$), while a variable that is a number does not contain a dollar sign (e.g. start, end). NB: Do not start a variable with a capital letter or a number! Praat will return an error.

To follow along, go to Joey’s website and download “Sample Audio”. Unzip the zip file, and load TextGrid WS006-Stephanie into Praat.

phoneTier = 1
labelInterval = 2

select TextGrid WS006-Stephanie

text$ = Get label of interval... phoneTier labelInterval

start = Get starting point: phoneTier, labelInterval
end = Get end point: phoneTier, labelInterval
duration = end - start
appendInfoLine: duration

This code will print the duration of the second segment of the phone tier (i.e. the first tier) to the Praat info window, as shown in Figure 4 below.

Figure 4. Info Window with Duration Output

Figure 4. Info Window with Duration Output

Your Turn!

The problem

What if you wanted to get the duration of the third interval on the phone tier?

The solution

In order to get information from different tiers, we can change the variables. First, we’ll change the labelInterval variable to be 3.

phoneTier = 1
labelInterval = 3

select TextGrid WS006-Stephanie

text$ = Get label of interval... phoneTier labelInterval

start = Get starting point: phoneTier, labelInterval
end = Get end point: phoneTier, labelInterval
duration = end - start
appendInfoLine: duration

3 Loops

3.1 What are loops?

What if you wanted to get the duration of multiple segments in your TextGrid? You can do this using a loop.

First, let’s learn what a loop is. A loop is just a basic programing concept that repeats a block of code. For example, you could count from 1 to 10 using a loop.

This example is saying let x be each number in the sequence from 1 to 10, and for each one, do the code from here until the end of the for loop, represented by endfor. x is just a variable, and could be replaced by a named variable describing what is contained in it. You should indent the code between the line starting with for and the endfor in order to visualize what code will be repeated in the loop.

for x from 1 to 10
  appendInfoLine: x
endfor

This code will print the numbers 1 through 10, as shown in Figure 5.

Figure 5. Info Window with Loop Output

Figure 5. Info Window with Loop Output

Your Turn!

The problem

Before we move on, let’s try out a loop. Write a script to write 5 sequential numbers from 45 to 49.

The solution
for x from 45 to 49
  appendInfoLine: x
endfor

3.2 Loops: an example

Now, let’s look at a more linguistic example. This example gets the label and duration of each interval, and provides output on the Praat info window.

This example also shows you that to output a number as part of a larger string (in a sentence, for example) you need to use the string$() function.

phoneTier = 1
select TextGrid WS006-Stephanie

number_of_intervals = Get number of intervals... phoneTier

for each_interval from 1 to number_of_intervals
  start = Get starting point: phoneTier, each_interval
  end = Get end point: phoneTier, each_interval
  duration = end - start
  phone$ = Get label of interval: phoneTier, each_interval
  appendInfoLine: "The duration of " + phone$ + " is " + string$(duration) + "."
endfor

This code will output the sentences below, shown in Figure 6.

Figure 6. Praat Info Window with Duration from All Phones

Figure 6. Praat Info Window with Duration from All Phones

4 Control flow

In programming, control flow refers to the order in which a script is executed.

For example, say you had a TextGrid that was annotated by phone, and you only wanted a measurement of the duration of the phone but only if the phone was a ‘P’. You’d want to loop through all the intervals of the phone tier of the TextGrid and make a measurement if the label of the text equals ‘P’

phoneTier = 1
select TextGrid WS006-Stephanie

number_of_intervals = Get number of intervals... phoneTier

for each_interval from 1 to number_of_intervals
  phone$ = Get label of interval: phoneTier, each_interval
  if phone$ = "P"
    # indent your code another time to keep track of what code is executed if the if statement is true
    start = Get starting point: phoneTier, each_interval
    end = Get end point: phoneTier, each_interval
    duration = end - start
    appendInfoLine: "The duration of " + phone$ + " is " + string$(duration) + "."
  endif
endfor

The above code will give the output shown in Figure 7.

Figure 7. Sample Output

Figure 7. Sample Output

In addition to if statements, there are also elsif (short for else if), and else statements. The elsif statement provides another condition (and other block of code) to run if the elsif statement is true. The else statement provides code to run if neither the if nor elsif statement is true. For example, say you wanted to categorize phones by if they are stops, vowels, or other consonants. To accomplish this, you also need to know that you can substitute an argument for a variable, and also that you can match a variable to a thing called a regular expression. You can learn more about this in the “Regular Expressions” section of the Praat manual. A regular expression is a way of defining a search pattern. For this example, you need to know that the caret (^) is a special character that anchors the match to the begining of the string, and that the dollar sign ($) is also a special character that anchors the match to the end of the string. For example, the regular expression “a” would match “a”, “ab”, “abc”, “ba”, “cba”, but the regular expression “^a$” would only match “a”. You also need to know that brackets ([]) represent optionality. For example, the regular expression [abc] would match “a” or “b”, or “c”. All vowel sounds in ARPAbet start with either “A”, “E”, “I”, “O”, or “U”, so we can use the regular expression [AEIOU] to match any of these characters. We’ll also define a blank regular expression that will match the blank spaces by matching with nothing between the start of the string (^) and the end of the string ($).

The function index_regex() finds where the second argument (the regular expression) matches the first argument (the string). We actually don’t care about where it matches, but since it will return a number, the if statement will evaluate it as true. See more about string functions in Praat in the manual.

phoneTier = 1
stops$ = "^[PTKBDG]$"
vowels$ = "[AEIOU]"
blank$ = "^$"

select TextGrid WS006-Stephanie

number_of_intervals = Get number of intervals... phoneTier

for each_interval from 1 to number_of_intervals
  phone$ = Get label of interval: phoneTier, each_interval
  start = Get starting point: phoneTier, each_interval
  end = Get end point: phoneTier, each_interval
  duration = end - start
  if index_regex(phone$, stops$)
    type$ = "stop"
  elsif index_regex(phone$, vowels$)
    type$ = "vowel"
  elsif index_regex(phone$, blank$)
    type$ = "blank"
  else
    type$ = "consonant"
  endif
  appendInfoLine: "The phone is " + phone$ + " and the type of sound is " + type$ + "."
endfor

One common mistake is forgetting to end if statements and for loops, so don’t forget to use endif after all your if/elsif/else statements, and endfor at the end of the code you want to repeat.

The output of this code is given below in Figure 8.

Figure 8. Sample Output

Figure 8. Sample Output

5 Reading in files

To read in a large number of files, I ask Praat to find all the files in a certain directory that have a .TextGrid extension. This is accomplished in line 9 of the code below (speakers = Create Strings as file list: "list", directory$ + "*.TextGrid") by using the wildcard, *, which just means any character any number of times (in this case, as long as a file has the .TextGrid extension Praat will get it’s file name). Then, you can use a for loop to read in all the files.

# where your textgrids live
directory$ = "/Users/lisalipani/desktop/sample_recordings/"

# '\' on windows, '/' on mac
sep$ = "/"

directory$ = "/Users/lisalipani/desktop/sample_recordings/"

speakers = Create Strings as file list: "list", directory$ + "*.TextGrid"
last = Get number of strings

for file from 1 to last
  selectObject: speakers
  textGrid$ = Get string... 'file'
  Read from file... 'directory$''textGrid$'
endfor

This code will read in all the TextGrids into you Praat Objects window, as shown in Figure 9 below.

Figure 9. TextGrids in Praat Objects Window

Figure 9. TextGrids in Praat Objects Window

6 Outputting files

Much like appendInfoLine:, you can use appendFileLine: in order to write your output for a file rather than the Praat info window.

First, specify your file name. If it doesn’t exist, Praat will create it for you. Let’s use all the code we have learned so far in order to read in all the textgrids, and output the file name, phone, word, and duration for all the vowels to the “results.txt” file.

This example also shows you how to retrieve the word from the wordTier based on the time.

I’ve also added a line to print a message to the Praat info window telling me that the script has finished running (otherwise, Praat will do nothing when it has finished).

# where your textgrids live
directory$ = "/Users/lisalipani/desktop/sample_recordings/"

# name of your results file
results$ = "results.txt"

# '\' on windows, '/' on mac
sep$ = "/"

# define where our tiers are
phoneTier = 1
wordTier = 2

# define our regular expressions to search
vowels$ = "[AEIOU]"

# read in our TextGrids
speakers = Create Strings as file list: "list", directory$ + "*.TextGrid"
last = Get number of strings

# read in each file
for file from 1 to last
  selectObject: speakers
  textGrid$ = Get string... 'file'
  Read from file... 'directory$''textGrid$'

  number_of_intervals = Get number of intervals... phoneTier

  for each_interval from 1 to number_of_intervals
    phone$ = Get label of interval: phoneTier, each_interval
 
    if index_regex(phone$, vowels$)
      start = Get starting point: phoneTier, each_interval
      end = Get end point: phoneTier, each_interval
      duration = end - start
      midpoint = end - duration/2
      wordNum = Get interval at time... wordTier midpoint
        word$ = Get label of interval: wordTier, wordNum
        appendFileLine: "'directory$''results$'", textGrid$, tab$, phone$, tab$, word$, tab$, duration
    endif
  endfor
endfor

appendInfoLine: "Your script has finished running! Hooray!"

A common error is not putting print statements in the correct loop. For example, our appendFileLine MUST happen inside the if statement for a few reasons. It should not happen in the for loop (because that is repeating code for each file, not interval). It should not be inside the second for loop either. This has to do with variable scope. For reasons beyond this scope of this workshop, Praat will not recognize the variables defined inside the if statement outside of the if statement (though Praat will recognize variables defined in higher-level loops (i.e. less indented) inside the lower-level if statement). If you are getting an error with many loops/if statements, check to make sure your for loops/if statements are actually repeating/executing the code you want them to.

This script will output a file - ‘results.txt’ - to the same directory with your TextGrids (though you could define any directory you want). Figure 10 shows what the first lines of this text file should look like.

Figure 10. Sample Output Text File

Figure 10. Sample Output Text File

7 Your Turn!

The rest of the workshop is for you to play around with the scripting skills you have just learned (maybe even on your own data!)

8 Next Time

Next time (October 2nd), we’ll look at how to use scripting to extract formant measurements, and in the 4th workshop (November 6th) we’ll look at extracting voice onset time from consonants.