Meas formants

From Phonlab
Revision as of 13:22, 15 October 2013 by Ronald (talk | contribs)
Jump to navigationJump to search
#!/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|AX|AXR|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(fromFile=tg, fromType='praat')

        # Create ifcformant results and read into ifc LabelManager.
        proc = subprocess.Popen(ifc_args + [fname + '.wav'], stderr=subprocess.PIPE)
        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(fromFile=tempifc, fromType='table', t1Col='sec')

        out.write(head)

        # Loop over all the vowel labels in the tier named 'phone'.
        for v, m in pm.tier('phone').search(vre, returnMatch=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').labelAt(v.center())
            context = pm.tier('context').labelAt(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.labelsAt(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)