Difference between revisions of "Meas formants"

From Phonlab
Jump to navigationJump to search
 
(9 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].
  +
  +
<code>meas_formants</code> is a sample script for doing formant analysis on an audio file, reading an associated Praat TextGrid, and extracting formant measurements from vowel tokens. You can copy the script and customize it to your own project.
  +
  +
See [[meas_formants_walkthrough]] for more detail on some of the concepts used in this script.
  +
 
<nowiki>
 
<nowiki>
 
#!/usr/bin/env python
 
#!/usr/bin/env python
   
 
# Measure formants. Read input textgrids, call ifcformant on associated audio file,
 
# Measure formants. Read input textgrids, call ifcformant on associated audio file,
# and output measurements to a .meas file.
+
# and output measurements to .meas files.
   
 
Usage = 'meas_formants speaker file1.TextGrid [file2.TextGrid] ... [fileN.TextGrid]'
 
Usage = 'meas_formants speaker file1.TextGrid [file2.TextGrid] ... [fileN.TextGrid]'
Line 24: Line 30:
 
# Regular expression used to identify vowels.
 
# Regular expression used to identify vowels.
 
vre = re.compile(
 
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)?"
+
"^(?P<vowel>AA|AE|AH|AO|AW|AXR|AX|AY|EH|ER|EY|IH|IX|IY|OW|OY|UH|UW|UX)(?P<stress>\d)?$"
 
)
 
)
  +
  +
# Mapping of column headings to column formats
  +
fldmap = (
  +
"t1", "0.4f",
  +
"t2", "0.4f",
  +
"lintime", "0.4f",
  +
"ifctime", "0.4f",
  +
"idx", "d",
  +
"vowel", "s",
  +
"stress", "s",
  +
"rms", "s",
  +
"f1", "s",
  +
"f2", "s",
  +
"f3", "s",
  +
"f4", "s",
  +
"f0", "s",
  +
"word", "s",
  +
"context", "s",
  +
)
   
 
# The output header line.
 
# The output header line.
head = '\t'.join(('t1 t2 lintime ifctime idx vowel stress \
+
head = '\t'.join(fldmap[0:len(fldmap):2]) + '\n'
rms f1 f2 f3 f4 f0 word context'
 
).split()) + '\n'
 
   
 
# Format string used for output.
 
# Format string used for output.
fmt = '\t'.join(["{t1:0.4f}", "{t2:0.4f}", "{lintime:0.4f}", "{ifctime:0.4f}",
+
fmt = '\t'.join( \
  +
[ \
"{idx:d}", "{vowel}", "{stress}", "{rms}", "{f1}", "{f2}",
 
"{f3}", "{f4}", "{f0}", "{word}", "{context}\n"
+
'{' + '{0}:{1}'.format(col,fmt) + '}' \
])
+
for col, fmt in zip( \
  +
fldmap[0:len(fldmap):2], \
  +
fldmap[1:len(fldmap):2] \
 
) \
  +
] \
  +
) + '\n'
   
 
# Name to use for ifcformant output file.
 
# Name to use for ifcformant output file.
Line 55: Line 83:
   
 
# Praat LabelManager.
 
# Praat LabelManager.
pm = audiolabel.LabelManager(fromFile=tg, fromType='praat')
+
pm = audiolabel.LabelManager(from_file=tg, from_type='praat')
   
 
# Create ifcformant results and read into ifc LabelManager.
 
# Create ifcformant results and read into ifc LabelManager.
proc = subprocess.Popen(ifc_args + [fname + '.wav'], stderr=subprocess.PIPE)
+
proc = subprocess.Popen(ifc_args + [fname + '.wav'])
 
proc.wait()
 
proc.wait()
 
if proc.returncode != 0:
 
if proc.returncode != 0:
Line 64: Line 92:
 
sys.stderr.write(line + '\n')
 
sys.stderr.write(line + '\n')
 
raise Exception("ifcformant exited with status: {0}".format(proc.returncode))
 
raise Exception("ifcformant exited with status: {0}".format(proc.returncode))
ifc = audiolabel.LabelManager(fromFile=tempifc, fromType='table', t1Col='sec')
+
ifc = audiolabel.LabelManager(from_file=tempifc, from_type='table', t1_col='sec')
   
 
out.write(head)
 
out.write(head)
   
 
# Loop over all the vowel labels in the tier named 'phone'.
 
# Loop over all the vowel labels in the tier named 'phone'.
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() > 1 and v.t1() != 0:
+
if v.duration > 1 and v.t1 != 0:
 
continue
 
continue
   
 
# Safer to use center of a label as reference point than an edge.
 
# Safer to use center of a label as reference point than an edge.
word = pm.tier('word').labelAt(v.center())
+
word = pm.tier('word').label_at(v.center)
context = pm.tier('context').labelAt(v.center())
+
context = pm.tier('context').label_at(v.center)
   
 
# Take measurements at start and end of vowel and at five
 
# Take measurements at start and end of vowel and at five
 
# equally-spaced intervals in between them.
 
# equally-spaced intervals in between them.
for idx, t in enumerate(np.linspace(v.t1(), v.t2(), num=7)):
+
for idx, t in enumerate(np.linspace(v.t1, v.t2, num=7)):
meas = ifc.labelsAt(t)
+
meas = ifc.labels_at(t)
out.write(fmt.format(t1=v.t1(), t2=v.t2(), lintime=t, ifctime=meas.f1.t1(),
+
out.write(fmt.format(t1=v.t1, t2=v.t2, lintime=t, ifctime=meas.f1.t1,
 
idx=idx, vowel=m.group('vowel'), stress=m.group('stress'),
 
idx=idx, vowel=m.group('vowel'), stress=m.group('stress'),
 
rms=meas.rms.text, f1=meas.f1.text,
 
rms=meas.rms.text, f1=meas.f1.text,
Line 89: Line 117:
 
)
 
)
 
os.unlink(tempifc)
 
os.unlink(tempifc)
  +
   
 
</nowiki>
 
</nowiki>

Latest revision as of 11:18, 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.

meas_formants is a sample script for doing formant analysis on an audio file, reading an associated Praat TextGrid, and extracting formant measurements from vowel tokens. You can copy the script and customize it to your own project.

See meas_formants_walkthrough for more detail on some of the concepts used in this script.

#!/usr/bin/env python

# Measure formants. Read input textgrids, call ifcformant on associated audio file,
# and output measurements to .meas files.

Usage = 'meas_formants speaker file1.TextGrid [file2.TextGrid] ... [fileN.TextGrid]'

import audiolabel
import os, sys, subprocess
import re
import numpy as np

# Do minimal checking to ensure the script was called with appropriate
# arguments.
try:
    if not (sys.argv[1] == 'male' or
            sys.argv[1] == 'female' or
            sys.argv[1] == 'child'): raise
    sys.argv[2] != None
except:
    raise Exception('Usage: ' + Usage)

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

# Mapping of column headings to column formats
fldmap = (
  "t1", "0.4f",
  "t2", "0.4f",
  "lintime", "0.4f",
  "ifctime", "0.4f",
  "idx", "d",
  "vowel", "s",
  "stress", "s",
  "rms", "s",
  "f1", "s",
  "f2", "s",
  "f3", "s",
  "f4", "s",
  "f0", "s",
  "word", "s",
  "context", "s",
)

# The output header line.
head = '\t'.join(fldmap[0:len(fldmap):2]) + '\n'

# Format string used for output.
fmt  = '\t'.join( \
           [ \
               '{' + '{0}:{1}'.format(col,fmt) + '}' \
               for col, fmt in zip( \
                       fldmap[0:len(fldmap):2], \
                       fldmap[1:len(fldmap):2] \
                   ) \
           ] \
       ) + '\n'

# Name to use for ifcformant output file.
tempifc = '__temp.ifc'

speaker = sys.argv[1]

ifc_args = ['ifcformant',
           '--speaker=' + speaker,
           '-e', 'gain -n -3 sinc -t 10 60 contrast',
           '--print-header',
           '--output=' + tempifc]

# Loop over all the input files specified on the command line.
for tg in sys.argv[2:]:
    fname = os.path.splitext(tg)[0]  # get filename without extension
    with open(fname + '.meas', 'w') as out:

        # Praat LabelManager.
        pm = audiolabel.LabelManager(from_file=tg, from_type='praat')

        # Create ifcformant results and read into ifc LabelManager.
        proc = subprocess.Popen(ifc_args + [fname + '.wav'])
        proc.wait()
        if proc.returncode != 0:
            for line in proc.stderr:
                sys.stderr.write(line + '\n')
            raise Exception("ifcformant exited with status: {0}".format(proc.returncode))
        ifc = audiolabel.LabelManager(from_file=tempifc, from_type='table', t1_col='sec')

        out.write(head)

        # Loop over all the vowel labels in the tier named 'phone'.
        for v, m in pm.tier('phone').search(vre, return_match=True):
            if v.duration > 1 and v.t1 != 0:
                continue

            # Safer to use center of a label as reference point than an edge.
            word = pm.tier('word').label_at(v.center)
            context = pm.tier('context').label_at(v.center)

            # Take measurements at start and end of vowel and at five
            # equally-spaced intervals in between them.
            for idx, t in enumerate(np.linspace(v.t1, v.t2, num=7)):
                meas = ifc.labels_at(t)
                out.write(fmt.format(t1=v.t1, t2=v.t2, lintime=t, ifctime=meas.f1.t1,
                                 idx=idx, vowel=m.group('vowel'), stress=m.group('stress'),
                                 rms=meas.rms.text, f1=meas.f1.text,
                                 f2=meas.f2.text, f3=meas.f3.text, f4=meas.f4.text,
                                 f0=meas.f0.text, word=word.text,
                                 context=context.text)
                )
    os.unlink(tempifc)