Difference between revisions of "Meas formants walkthrough"

From Phonlab
Jump to navigationJump to search
m
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
  +
This page shows the old style of using <code>audiolabel</code>, which is not recommended. For a better approach see the notebooks on [https://github.com/rsprouse/audiolabel/blob/master/doc/working_with_phonetic_dataframes.ipynb audiolabel's read_label function] and [https://github.com/rsprouse/phonlab/blob/master/doc/Automated%20formant%20measurements.ipynb automated formant measurements].
This page walks you through many of the concepts used in [[meas_formants]].
 
   
  +
You can download <code>audiolabel</code> from its [https://github.com/rsprouse/audiolabel github repository].
Here are annotated snippets of a script that reads Praat TextGrids, searches for
 
  +
  +
This page walks you through many of the concepts used in [[meas_formants]], a
  +
script that reads Praat TextGrids, searches for
 
vowel tokens, and runs a formant analyzer. Formant measurements are extracted
 
vowel tokens, and runs a formant analyzer. Formant measurements are extracted
 
from the results and written to a text file, along with additional metadata
 
from the results and written to a text file, along with additional metadata
 
retrieved from the TextGrids.
 
retrieved from the TextGrids.
   
Let's take a look at this in pseudocode first:
+
Let's take a look at this in pseudocode first for a conceptual overview of the program flow:
   
 
read in a textgrid
 
read in a textgrid
 
run the ifcformant system command
 
run the ifcformant system command
 
read the ifcformant output
 
read the ifcformant output
 
 
search the textgrid for vowel tokens (V):
 
search the textgrid for vowel tokens (V):
 
if V meets size condition:
 
if V meets size condition:
Line 21: Line 23:
 
'phone', 'word', and 'context'.
 
'phone', 'word', and 'context'.
   
## Getting started
+
== Getting started ==
   
 
First, import the necessary libraries:
 
First, import the necessary libraries:
Line 32: Line 34:
 
reload(audiolabel)
 
reload(audiolabel)
   
  +
'''Output:'''
 
 
 
 
<module 'audiolabel' from 'audiolabel.py'>
 
<module 'audiolabel' from 'audiolabel.py'>
   
   
  +
== Read in a TextGrid ==
 
## Read in a TextGrid
 
   
 
Our script assumes that TextGrids reside in the same directory as the
 
Our script assumes that TextGrids reside in the same directory as the
Line 46: Line 45:
   
   
tg = 'doc/this_is_a_label_file.TextGrid' # name of Praat TextGrid
+
tg = '../test/this_is_a_label_file.TextGrid' # name of Praat TextGrid
 
fname = os.path.splitext(tg)[0] # get filename without extension
 
fname = os.path.splitext(tg)[0] # get filename without extension
 
fname
 
fname
   
  +
'''Output:'''
 
  +
'../test/this_is_a_label_file'
 
 
'doc/this_is_a_label_file'
 
 
   
   
Line 61: Line 57:
 
can distinguish <code>'short'</code> and <code>'long'</code> Praat formats. If
 
can distinguish <code>'short'</code> and <code>'long'</code> Praat formats. If
 
you want to be explicit, you can use <code>'praat_short'</code> or
 
you want to be explicit, you can use <code>'praat_short'</code> or
<code>'praat_long'</code> as the <code>fromType</code>.
+
<code>'praat_long'</code> as the <code>from_type</code>.
 
 
pm = audiolabel.LabelManager(fromFile=tg, fromType='praat')
 
lab = pm.tier('word').labelAt(0.5736)
 
lab.center()
 
 
   
   
  +
pm = audiolabel.LabelManager(from_file=tg, from_type='praat')
  +
lab = pm.tier('word').label_at(0.5736)
  +
lab.center
   
  +
'''Output:'''
 
0.621615744181556
 
0.621615744181556
   
  +
== Run the ifcformant system command ==
 
 
## Run the ifcformant system command
 
   
 
<code>ifcformant</code> is our formant analysis program. It is a separate
 
<code>ifcformant</code> is our formant analysis program. It is a separate
Line 85: Line 77:
 
included as the first item in the list.
 
included as the first item in the list.
   
  +
tempifc = '__temp.ifc' # output destination of ifcformant command
 
tempifc = 'doc/__temp.ifc' # output destination of ifcformant command
 
 
speaker = 'male' # gender of speaker
 
speaker = 'male' # gender of speaker
 
ifc_args = ['ifcformant', # list of arguments
 
ifc_args = ['ifcformant', # list of arguments
Line 96: Line 87:
 
ifc_args
 
ifc_args
   
  +
'''Output:'''
 
 
 
 
['ifcformant',
 
['ifcformant',
 
'--speaker=male',
 
'--speaker=male',
Line 105: Line 94:
 
'--print-header',
 
'--print-header',
 
'--output=doc/__temp.ifc',
 
'--output=doc/__temp.ifc',
'doc/this_is_a_label_file.wav']
+
'../test/this_is_a_label_file.wav']
 
 
   
 
We use <code>Popen</code> to execute <code>ifcformant</code> and return a handle
 
We use <code>Popen</code> to execute <code>ifcformant</code> and return a handle
Line 117: Line 104:
 
<code>subprocess.PIPE</code> so that we can report the errors if
 
<code>subprocess.PIPE</code> so that we can report the errors if
 
<code>ifcformant</code> fails.
 
<code>ifcformant</code> fails.
 
   
 
proc = subprocess.Popen(ifc_args, stderr=subprocess.PIPE)
 
proc = subprocess.Popen(ifc_args, stderr=subprocess.PIPE)
 
proc.wait()
 
proc.wait()
   
  +
'''Output:'''
 
 
 
 
0
 
0
 
 
   
 
Finally, we check the process's return code to see whether
 
Finally, we check the process's return code to see whether
 
<code>ifcformant</code> succeeded, in which case the code is <code>0</code>. If
 
<code>ifcformant</code> succeeded, in which case the code is <code>0</code>. If
 
<code>ifcformant</code> failed, we report the error and raise an exception.
 
<code>ifcformant</code> failed, we report the error and raise an exception.
 
   
 
if proc.returncode != 0:
 
if proc.returncode != 0:
Line 139: Line 120:
 
raise Exception("ifcformant exited with status: {0}".format(proc.returncode))
 
raise Exception("ifcformant exited with status: {0}".format(proc.returncode))
   
## Read the <code>ifcformant</code> output
+
== Read the <code>ifcformant</code> output ==
   
 
<code>ifcformant</code> produces a table of numbers. Since we included the <code
 
<code>ifcformant</code> produces a table of numbers. Since we included the <code
Line 145: Line 126:
 
fields. Here are the first 10 lines of output:
 
fields. Here are the first 10 lines of output:
   
  +
!head __temp.ifc
   
  +
'''Output:'''
!head doc/__temp.ifc
 
 
 
sec rms f1 f2 f3 f4 f0
 
sec rms f1 f2 f3 f4 f0
 
0.0050 0.0 0.0 0.0 0.0 0.0 0.0
 
0.0050 0.0 0.0 0.0 0.0 0.0 0.0
Line 162: Line 143:
 
If you think about it, this output is a kind of label file. We have six columns
 
If you think about it, this output is a kind of label file. We have six columns
 
of output for each timepoint, and we can think of these as six label tiers.
 
of output for each timepoint, and we can think of these as six label tiers.
<code>LabelManager</code> provides a <code>readTable()</code> method for reading
+
<code>LabelManager</code> provides a <code>read_table()</code> method for reading
 
in tabular data, and we can invoke it automatically by specifying
 
in tabular data, and we can invoke it automatically by specifying
<code>fromType='table'</code> when creating the <code>LabelManager</code>.
+
<code>from_type='table'</code> when creating the <code>LabelManager</code>.
   
The <code>t1Col='sec'</code> argument tells <code>readTable()</code> that each
+
The <code>t1_col='sec'</code> argument tells <code>read_table()</code> that each
 
<code>Label</code>'s <code>t1</code> (see below) is identified by the column
 
<code>Label</code>'s <code>t1</code> (see below) is identified by the column
 
labelled <code>'sec'</code>.
 
labelled <code>'sec'</code>.
   
  +
ifc = audiolabel.LabelManager(from_file=tempifc, from_type='table', t1_col='sec')
  +
ifc.tier('f1').label_at(1.620)
   
  +
'''Output:'''
ifc = audiolabel.LabelManager(fromFile=tempifc, fromType='table', t1Col='sec')
 
  +
<b>Label</b>( <b>t1</b>=1.6250, <b>text</b>='445.7' )
ifc.tier('f1').labelAt(1.620)
 
   
  +
== Search the textgrid for vowel tokens ==
 
 
 
<b>Label</b>( <b>t1</b>=1.6250, <b>text</b>='445.7' )
 
 
 
 
## Search the textgrid for vowel tokens
 
   
 
We want to extract formant measurements during sections of the audio file that
 
We want to extract formant measurements during sections of the audio file that
Line 195: Line 171:
 
vre
 
vre
   
  +
'''Output:'''
 
 
 
 
re.compile(r'(?P<vowel>AA|AE|AH|AO|AW|AX|AXR|AY|EH|ER|EY|IH|IX|IY|OW|OY|UH|UW|UX)(?P<stress>\d)?')
 
re.compile(r'(?P<vowel>AA|AE|AH|AO|AW|AX|AXR|AY|EH|ER|EY|IH|IX|IY|OW|OY|UH|UW|UX)(?P<stress>\d)?')
 
 
   
 
A <code>LabelManager tier</code> is a Sequence, which allows for easy access to
 
A <code>LabelManager tier</code> is a Sequence, which allows for easy access to
 
labels in a loop:
 
labels in a loop:
 
   
 
for lab in pm.tier('context'):
 
for lab in pm.tier('context'):
 
print lab.text
 
print lab.text
   
  +
'''Output:'''
 
 
1
 
1
 
2
 
2
Line 221: Line 192:
 
tokens
 
tokens
   
  +
'''Output:'''
 
 
 
 
[Label( t1=0.1850, t2=0.3058, text='IH2' ),
 
[Label( t1=0.1850, t2=0.3058, text='IH2' ),
 
Label( t1=0.4208, t2=0.5183, text='IH0' ),
 
Label( t1=0.4208, t2=0.5183, text='IH0' ),
Line 232: Line 201:
 
In our script we also want to return the named captures from our match. We
 
In our script we also want to return the named captures from our match. We
 
change the return values of <code>search()</code> with the
 
change the return values of <code>search()</code> with the
<code>returnMatch</code> parameter.
+
<code>return_match</code> parameter. You can see that <code>search()</code> now returns
  +
tuples of <code>Label</code> objects and associated <code>Match</code> objects. Notice
  +
the parentheses surrounding each pair.
   
   
tokens = pm.tier('phone').search(vre, returnMatch=True)
+
tokens = pm.tier('phone').search(vre, return_match=True)
 
tokens
 
tokens
   
  +
'''Output:'''
 
 
 
 
[(Label( t1=0.1850, t2=0.3058, text='IH2' ), <_sre.SRE_Match at 0x5c00828>),
 
[(Label( t1=0.1850, t2=0.3058, text='IH2' ), <_sre.SRE_Match at 0x5c00828>),
 
(Label( t1=0.4208, t2=0.5183, text='IH0' ), <_sre.SRE_Match at 0x5c00d78>),
 
(Label( t1=0.4208, t2=0.5183, text='IH0' ), <_sre.SRE_Match at 0x5c00d78>),
Line 248: Line 217:
   
 
It is simple to set up a loop to access each matching <code>Label</code> and its
 
It is simple to set up a loop to access each matching <code>Label</code> and its
  +
associated match data. This <code>for</code> loop iterates over every vowel and match in the list returned by <code>search()</code>.
associated match data.
 
   
   
for v, m in pm.tier('phone').search(vre, returnMatch=True):
+
for v, m in pm.tier('phone').search(vre, return_match=True):
print v.t1(), '-', v.t2()
+
print v.t1, '-', v.t2
 
print v.text
 
print v.text
 
print m.group('vowel')
 
print m.group('vowel')
Line 258: Line 227:
 
print
 
print
   
  +
'''Output:'''
 
0.185027889005 - 0.305817195604
 
0.185027889005 - 0.305817195604
 
IH2
 
IH2
Line 278: Line 248:
   
   
for v, m in pm.tier('phone').search(vre, returnMatch=True):
+
for v, m in pm.tier('phone').search(vre, return_match=True):
if v.duration() > 0.1:
+
if v.duration > 0.1:
print v.t1(), '-', v.t2()
+
print v.t1, '-', v.t2()
 
print v.text
 
print v.text
 
print m.group('vowel')
 
print m.group('vowel')
Line 286: Line 256:
 
print
 
print
   
  +
'''Output:'''
 
0.185027889005 - 0.305817195604
 
0.185027889005 - 0.305817195604
 
IH2
 
IH2
Line 295: Line 266:
 
The <code>'word'</code> and <code>'context'</code> tiers can be queried to
 
The <code>'word'</code> and <code>'context'</code> tiers can be queried to
 
return the <code>Label</code> that occurs at the time of the vowel token with
 
return the <code>Label</code> that occurs at the time of the vowel token with
the <code>labelAt</code> method.
+
the <code>label_at</code> method.
   
   
for v, m in pm.tier('phone').search(vre, returnMatch=True):
+
for v, m in pm.tier('phone').search(vre, return_match=True):
if v.duration() > 0.1:
+
if v.duration > 0.1:
 
print v.text
 
print v.text
word = pm.tier('word').labelAt(v.center())
+
word = pm.tier('word').label_at(v.center)
 
print word.text
 
print word.text
context = pm.tier('context').labelAt(v.center())
+
context = pm.tier('context').label_at(v.center)
 
print context.text
 
print context.text
 
 
  +
'''Output:'''
 
 
IH2
 
IH2
 
This
 
This
 
1
 
1
 
   
## Get regularly-sampled formant measurements
+
== Get regularly-sampled formant measurements ==
   
 
For this script we want formant measurments at the start and end of each vowel
 
For this script we want formant measurments at the start and end of each vowel
Line 319: Line 289:
   
   
for v, m in pm.tier('phone').search(vre, returnMatch=True):
+
for v, m in pm.tier('phone').search(vre, return_match=True):
if v.duration() > 0.1:
+
if v.duration > 0.1:
points = np.linspace(v.t1(), v.t2(), num=7)
+
points = np.linspace(v.t1, v.t2, num=7)
 
print points
 
print points
   
  +
'''Output:'''
 
[ 0.18502789 0.20515944 0.22529099 0.24542254 0.26555409 0.28568564
 
[ 0.18502789 0.20515944 0.22529099 0.24542254 0.26555409 0.28568564
 
0.3058172 ]
 
0.3058172 ]
 
 
   
The <code>LabelManager</code> has a <code>labelsAt</code> method that calls the
+
The <code>LabelManager</code> has a <code>labels_at</code> method that calls the
<code>labelAt</code> method on every tier managed by the
+
<code>label_at</code> method on every tier managed by the
 
<code>LabelManager</code> and returns a tuple of the results. If the tiers are
 
<code>LabelManager</code> and returns a tuple of the results. If the tiers are
 
named, the elements of the tuple can be accessed with the corresponding name.
 
named, the elements of the tuple can be accessed with the corresponding name.
   
   
for v, m in pm.tier('phone').search(vre, returnMatch=True):
+
for v, m in pm.tier('phone').search(vre, return_match=True):
if v.duration() > 0.1:
+
if v.duration > 0.1:
for t in np.linspace(v.t1(), v.t2(), num=7):
+
for t in np.linspace(v.t1, v.t2, num=7):
meas = ifc.labelsAt(t)
+
meas = ifc.labels_at(t)
 
print meas.f1.text
 
print meas.f1.text
   
  +
'''Output:'''
 
237.8
 
237.8
 
425.9
 
425.9
Line 348: Line 320:
 
474.9
 
474.9
 
 
  +
  +
== Get word and context from the TextGrid ==
   
 
This is a format string that is used to output results. The field names are in
 
This is a format string that is used to output results. The field names are in
 
curly brackets, and the portion after the colon specifies formatting details for
 
curly brackets, and the portion after the colon specifies formatting details for
 
numeric formats. Output is tab-separated.
 
numeric formats. Output is tab-separated.
 
## Get word and context from the TextGrid
 
 
 
 
   
 
fmt = '\t'.join(["{t1:0.4f}", "{t2:0.4f}", "{lintime:0.4f}", "{ifctime:0.4f}",
 
fmt = '\t'.join(["{t1:0.4f}", "{t2:0.4f}", "{lintime:0.4f}", "{ifctime:0.4f}",
Line 364: Line 333:
 
fmt
 
fmt
   
  +
'''Output:'''
 
  +
'{t1:0.4f}\t{t2:0.4f}\t{lintime:0.4f}\t{ifctime:0.4f}\t{idx:d}\t{vowel}\t{stress}\t{rms}\t{f1}\t{f2}\t{f3}\t{f4}\t{f0}\t{word}\t{context}\n'
 
 
'{t1:0.4f}\t{t2:0.4f}\t{lintime:0.4f}\t{ifctime:0.4f}\t{idx:d}\t{vowel}\t{stress}\t{rms}\t{f1}\t{f2}\t{f3}\t{f4}\t{f0}\t{word}\t{context}\n'
 
   
   
Line 382: Line 349:
 
head
 
head
 
 
  +
'''Output:'''
 
 
 
 
 
't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'
 
't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'
 
   
   
Line 394: Line 357:
 
head2
 
head2
   
  +
'''Output:'''
 
 
 
 
't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'
 
't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'

Latest revision as of 11:19, 11 April 2022

This page shows the old style of using audiolabel, which is not recommended. For a better approach see the notebooks on audiolabel's read_label function and automated formant measurements.

You can download audiolabel from its github repository.

This page walks you through many of the concepts used in meas_formants, a script that reads Praat TextGrids, searches for vowel tokens, and runs a formant analyzer. Formant measurements are extracted from the results and written to a text file, along with additional metadata retrieved from the TextGrids.

Let's take a look at this in pseudocode first for a conceptual overview of the program flow:

   read in a textgrid
   run the ifcformant system command
   read the ifcformant output
   search the textgrid for vowel tokens (V):
       if V meets size condition:
           get word and context of V from the textgrid
           get regularly-sampled formant measurements for V:
               output measurement with word and context

Our input TextGrids will have three interval tiers each, with tier names 'phone', 'word', and 'context'.

Getting started

First, import the necessary libraries:


   import audiolabel     # library for reading phonetic label files
   import os, subprocess     # access to system commands
   import re             # regular expressions
   import numpy as np    # numeric processing routines
   reload(audiolabel)

Output:

   <module 'audiolabel' from 'audiolabel.py'>


Read in a TextGrid

Our script assumes that TextGrids reside in the same directory as the .wav files they annotate, and that they share the same name, except for the extension.


   tg = '../test/this_is_a_label_file.TextGrid'   # name of Praat TextGrid
   fname = os.path.splitext(tg)[0]  # get filename without extension
   fname

Output:

   '../test/this_is_a_label_file'


Now we create a LabelManager by reading in the TextGrid with the hint that the file type is 'praat'. The LabelManager can distinguish 'short' and 'long' Praat formats. If you want to be explicit, you can use 'praat_short' or 'praat_long' as the from_type.


   pm = audiolabel.LabelManager(from_file=tg, from_type='praat')
   lab = pm.tier('word').label_at(0.5736)
   lab.center

Output:

   0.621615744181556

Run the ifcformant system command

ifcformant is our formant analysis program. It is a separate executable available on the system.

We will use Python's subprocess module to execute the command and check the return code. To set this up we'll make a list of arguments to pass to subprocess, with the ifcformant command itself included as the first item in the list.

   tempifc = '__temp.ifc' # output destination of ifcformant command
   speaker = 'male'           # gender of speaker
   ifc_args = ['ifcformant',  # list of arguments
              '--speaker=' + speaker,
              '-e', 'gain -n -3 sinc -t 10 60 contrast',
              '--print-header',
              '--output=' + tempifc,
              fname + '.wav']
   ifc_args

Output:

   ['ifcformant',
    '--speaker=male',
    '-e',
    'gain -n -3 sinc -t 10 60 contrast',
    '--print-header',
    '--output=doc/__temp.ifc',
    '../test/this_is_a_label_file.wav']

We use Popen to execute ifcformant and return a handle to the running process. We need to wait() for the process to finish before continuing on with our script. ifcformant can take a while to finish, so this is important!

Any errors reported by ifcformant are connected to subprocess.PIPE so that we can report the errors if ifcformant fails.

   proc = subprocess.Popen(ifc_args, stderr=subprocess.PIPE)
   proc.wait()

Output:

   0

Finally, we check the process's return code to see whether ifcformant succeeded, in which case the code is 0. If ifcformant failed, we report the error and raise an exception.

   if proc.returncode != 0:
       for line in proc.stderr:
           sys.stderr.write(line + '\n')
       raise Exception("ifcformant exited with status: {0}".format(proc.returncode))

Read the ifcformant output

ifcformant produces a table of numbers. Since we included the --print-header argument the output contains a header row identifying the fields. Here are the first 10 lines of output:

   !head __temp.ifc

Output:

   sec	rms	f1	f2	f3	f4	f0
   0.0050	0.0	0.0	0.0	0.0	0.0	0.0
   0.0150	0.0	0.0	0.0	0.0	0.0	0.0
   0.0250	0.0	0.0	0.0	0.0	0.0	0.0
   0.0350	75.0	354.4	1078.2	2368.6	3173.0	0.0
   0.0450	75.0	386.1	1427.1	2427.6	3291.3	0.0
   0.0550	82.2	473.6	1536.8	2525.5	3369.1	240.0
   0.0650	82.2	450.1	1481.2	2584.4	3366.3	106.2
   0.0750	240.8	526.8	1505.8	2572.2	3333.2	193.5
   0.0850	766.0	500.5	1580.9	2337.8	3358.4	150.0
   

If you think about it, this output is a kind of label file. We have six columns of output for each timepoint, and we can think of these as six label tiers. LabelManager provides a read_table() method for reading in tabular data, and we can invoke it automatically by specifying from_type='table' when creating the LabelManager.

The t1_col='sec' argument tells read_table() that each Label's t1 (see below) is identified by the column labelled 'sec'.

   ifc = audiolabel.LabelManager(from_file=tempifc, from_type='table', t1_col='sec')
   ifc.tier('f1').label_at(1.620)

Output:

   Label( t1=1.6250, text='445.7' )

Search the textgrid for vowel tokens

We want to extract formant measurements during sections of the audio file that are identified as vowels. We set up a regular expression that matches vowel tokens in the TextGrid's 'phone' tier. This regex contains two named capture groups, 'vowel' and 'stress', that will be returned when a matching label is found.


   vre = re.compile(
            "(?P<vowel>AA|AE|AH|AO|AW|AX|AXR|AY|EH|ER|EY|IH|IX|IY|OW|OY|UH|UW|UX)(?P<stress>\d)?"
         )
   vre

Output:

   re.compile(r'(?P<vowel>AA|AE|AH|AO|AW|AX|AXR|AY|EH|ER|EY|IH|IX|IY|OW|OY|UH|UW|UX)(?P<stress>\d)?')

A LabelManager tier is a Sequence, which allows for easy access to labels in a loop:

   for lab in pm.tier('context'):
       print lab.text

Output:

   1
   2
   

A LabelManager tier has a search() method that applies a regex and returns every Label that matches.


   tokens = pm.tier('phone').search(vre)
   tokens

Output:

   [Label( t1=0.1850, t2=0.3058, text='IH2' ),
    Label( t1=0.4208, t2=0.5183, text='IH0' ),
    Label( t1=0.5736, t2=0.6696, text='AH1' )]


In our script we also want to return the named captures from our match. We change the return values of search() with the return_match parameter. You can see that search() now returns tuples of Label objects and associated Match objects. Notice the parentheses surrounding each pair.


   tokens = pm.tier('phone').search(vre, return_match=True)
   tokens

Output:

   [(Label( t1=0.1850, t2=0.3058, text='IH2' ), <_sre.SRE_Match at 0x5c00828>),
    (Label( t1=0.4208, t2=0.5183, text='IH0' ), <_sre.SRE_Match at 0x5c00d78>),
    (Label( t1=0.5736, t2=0.6696, text='AH1' ), <_sre.SRE_Match at 0x5f96140>)]


It is simple to set up a loop to access each matching Label and its associated match data. This for loop iterates over every vowel and match in the list returned by search().


   for v, m in pm.tier('phone').search(vre, return_match=True):
       print v.t1, '-', v.t2
       print v.text
       print m.group('vowel')
       print m.group('stress')
       print

Output:

   0.185027889005 - 0.305817195604
   IH2
   IH
   2
   
   0.4207853308 - 0.51828995179
   IH0
   IH
   0
   
   0.573591080112 - 0.669640408251
   AH1
   AH
   1
   
   

A conditional expression restricts the results to other criteria:


   for v, m in pm.tier('phone').search(vre, return_match=True):
       if v.duration > 0.1:
           print v.t1, '-', v.t2()
           print v.text
           print m.group('vowel')
           print m.group('stress')
           print 

Output:

   0.185027889005 - 0.305817195604
   IH2
   IH
   2
   
   

The 'word' and 'context' tiers can be queried to return the Label that occurs at the time of the vowel token with the label_at method.


   for v, m in pm.tier('phone').search(vre, return_match=True):
       if v.duration > 0.1:
           print v.text
           word = pm.tier('word').label_at(v.center)
           print word.text
           context = pm.tier('context').label_at(v.center)
           print context.text
           

Output:

   IH2
   This
   1

Get regularly-sampled formant measurements

For this script we want formant measurments at the start and end of each vowel token, plus five equally-spaced timepoints in between. The linspace function from numpy simplifies the calculation of these timepoints:


   for v, m in pm.tier('phone').search(vre, return_match=True):
       if v.duration > 0.1:
           points = np.linspace(v.t1, v.t2, num=7)
           print points

Output:

   [ 0.18502789  0.20515944  0.22529099  0.24542254  0.26555409  0.28568564
     0.3058172 ]
   

The LabelManager has a labels_at method that calls the label_at method on every tier managed by the LabelManager and returns a tuple of the results. If the tiers are named, the elements of the tuple can be accessed with the corresponding name.


   for v, m in pm.tier('phone').search(vre, return_match=True):
       if v.duration > 0.1:
           for t in np.linspace(v.t1, v.t2, num=7):
               meas = ifc.labels_at(t)
               print meas.f1.text

Output:

   237.8
   425.9
   416.8
   476.5
   503.4
   510.5
   474.9
   

Get word and context from the TextGrid

This is a format string that is used to output results. The field names are in curly brackets, and the portion after the colon specifies formatting details for numeric formats. Output is tab-separated.

   fmt = '\t'.join(["{t1:0.4f}", "{t2:0.4f}", "{lintime:0.4f}", "{ifctime:0.4f}",
                   "{idx:d}", "{vowel}", "{stress}", "{rms}", "{f1}", "{f2}",
                   "{f3}", "{f4}", "{f0}", "{word}", "{context}\n"
                  ])
   fmt

Output:

    '{t1:0.4f}\t{t2:0.4f}\t{lintime:0.4f}\t{ifctime:0.4f}\t{idx:d}\t{vowel}\t{stress}\t{rms}\t{f1}\t{f2}\t{f3}\t{f4}\t{f0}\t{word}\t{context}\n'


And this creates our header line. As with the fmt assignment, we join the fields with a tab. Tabs could be inserted directly in to the string instead of using join() and split(), but the extra syntax makes the field names easier to pick out for a human reader.


   head = '\t'.join(('t1 t2 lintime ifctime idx vowel stress \
                      rms f1 f2 f3 f4 f0 word context'
                    ).split()) + '\n'
   head
   

Output:

   't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'


   head2 = 't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'
   head2

Output:

   't1\tt2\tlintime\tifctime\tidx\tvowel\tstress\trms\tf1\tf2\tf3\tf4\tf0\tword\tcontext\n'