Difference between revisions of "Meas formants walkthrough"
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 == |
|
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 = ' |
+ | 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> |
+ | <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', |
||
− | ' |
+ | '../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 == |
|
<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> |
+ | <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> |
+ | <code>from_type='table'</code> when creating the <code>LabelManager</code>. |
− | The <code> |
+ | 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> |
+ | <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, |
+ | 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, |
+ | for v, m in pm.tier('phone').search(vre, return_match=True): |
− | print v.t1 |
+ | 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, |
+ | for v, m in pm.tier('phone').search(vre, return_match=True): |
− | if v.duration |
+ | if v.duration > 0.1: |
− | print v.t1 |
+ | 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> |
+ | the <code>label_at</code> method. |
− | for v, m in pm.tier('phone').search(vre, |
+ | for v, m in pm.tier('phone').search(vre, return_match=True): |
− | if v.duration |
+ | if v.duration > 0.1: |
print v.text |
print v.text |
||
− | word = pm.tier('word'). |
+ | word = pm.tier('word').label_at(v.center) |
print word.text |
print word.text |
||
− | context = pm.tier('context'). |
+ | 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 == |
|
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, |
+ | for v, m in pm.tier('phone').search(vre, return_match=True): |
− | if v.duration |
+ | if v.duration > 0.1: |
− | points = np.linspace(v.t1 |
+ | 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> |
+ | The <code>LabelManager</code> has a <code>labels_at</code> method that calls the |
− | <code> |
+ | <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, |
+ | for v, m in pm.tier('phone').search(vre, return_match=True): |
− | if v.duration |
+ | if v.duration > 0.1: |
− | for t in np.linspace(v.t1 |
+ | for t in np.linspace(v.t1, v.t2, num=7): |
− | meas = ifc. |
+ | 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'