Meas formants

From Phonlab
Revision as of 11:18, 11 April 2022 by Ronald (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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)