mPraat demonstration

Contents

mPraat and rPraat homepage

rPraat and mPraat homepage: https://fu.ff.cuni.cz/praat/

Toolbox mPraat at github: https://github.com/bbTomas/mPraat

How to cite this toolbox?

We have invested a lot of time and effort in creating mPraat toolbox / rPraat package, please cite it when using it for data analysis.

Bořil, T., & Skarnitzl, R. (2016). Tools rPraat and mPraat. In P. Sojka, A. Horák, I. Kopeček, & K. Pala (Eds.), Text, Speech, and Dialogue (pp. 367–374). Springer International Publishing.

Download Tools rPraat and mPraat manuscript .pdf: https://fu.ff.cuni.cz/praat/boril_skarnitzl_2016_Tools_rPraat_and_mPraat_[manuscript].pdf

The final publication is available at Springer via https://dx.doi.org/10.1007/978-3-319-45510-5_42

Download BibTeX reference mpraatrpraat2016.bib: https://fu.ff.cuni.cz/praat/mpraatrpraat2016.bib

Installation and help

Download mPraat: mPraat-master.zip https://github.com/bbTomas/mPraat/archive/master.zip

To install mPraat, just unzip all files to any directory of your choice and Set Path (in Matlab) to this directory.

For help and examples, use command

help functionName

- or -

doc functionName

Read and plot TextGrid, PitchTier and Sound

tg = tgRead('demo/H.TextGrid');
figure, tgPlot(tg)

pt = ptRead('demo/H.PitchTier');
figure, ptPlot(pt);

[snd, fs] = audioread('demo/H.wav');
t = 0: 1/fs: (length(snd)-1)/fs;

figure, tgPlot(tg, 3);   % plot TextGrid with 2 empty subplots
nTiers = tgGetNumberOfTiers(tg);

subplot(nTiers+2, 1, 1); % plot Sound
plot(t, snd); axis tight

subplot(nTiers+2, 1, 2); % plot PitchTier
ptPlot(pt);

TextGrid

tg = tgRead('demo/H.TextGrid');

tiers = tgGetNumberOfTiers(tg)
tiers = length(tg.tier)
duration = tgGetTotalDuration(tg)

tier1name = tgGetTierName(tg, 1)
tiers =

     5


tiers =

     5


duration =

    3.6160


tier1name =

    'phoneme'

Tier accessed both by index and name (TextGrid)

tgIsPointTier(tg, 1)
tgIsPointTier(tg, 'phoneme')

tgIsIntervalTier(tg, 1)
tgIsIntervalTier(tg, 'phoneme')

type = tg.tier{1}.type
type = tg.tier{tgI(tg, 'phoneme')}.type
ans =

  logical

   1


ans =

  logical

   1


ans =

  logical

   0


ans =

  logical

   0


type =

    'point'


type =

    'point'

Point tier (TextGrid)

numPoints = tgGetNumberOfPoints(tg, 1)
numPoints = tgGetNumberOfPoints(tg, 'phoneme')
time4 = tgGetPointTime(tg, 'phoneme', 4)
label4 = tgGetLabel(tg, 'phoneme', 4)
numPoints =

    43


numPoints =

    43


time4 =

    0.3235


label4 =

    'i'

Point tier 'low-level access' (TextGrid)

numPoints = length(tg.tier{1}.T)   % or: numPoints = length(tg.tier{tgI(tg, 'phoneme')}.T)
time4 = tg.tier{1}.T(4)
label4 = tg.tier{1}.Label{4}

t = tg.tier{1}.T(5:8)
numPoints =

    43


time4 =

    0.3235


label4 =

    'i'


t =

    0.3750    0.4354    0.5043    0.5627

Interval tier (TextGrid)

tierIndex = tgI(tg, 'word')
tgIsPointTier(tg, 'word')
tgIsIntervalTier(tg, 'word')
type = tg.tier{4}.type   % or: type = tg.tier{tgI(tg, 'word')}.type

tierDuration = tgGetTotalDuration(tg, 'word')
tStart = tgGetStartTime(tg, 'word')
tEnd = tgGetEndTime(tg, 'word')

numIntervals = tgGetNumberOfIntervals(tg, 'word')
t1 = tgGetIntervalStartTime(tg, 'word', 4)
t2 = tgGetIntervalStartTime(tg, 'word', 4)
dur = tgGetIntervalDuration(tg, 'word', 4)
label = tgGetLabel(tg, 'word', 4)
tierIndex =

     4


ans =

  logical

   0


ans =

  logical

   1


type =

    'interval'


tierDuration =

    3.6080


tStart =

    0.0080


tEnd =

    3.6160


numIntervals =

    13


t1 =

    0.3522


t2 =

    0.3522


dur =

    0.2800


label =

    'řeknu'

Interval tier 'low-level access' (TextGrid)

numIntervals = length(tg.tier{4}.T1)
t1 = tg.tier{4}.T1(4)
t2 = tg.tier{4}.T2(4)
label = tg.tier{4}.Label{4}

lab = tg.tier{4}.Label(5:8)
numIntervals =

    13


t1 =

    0.3522


t2 =

    0.6322


label =

    'řeknu'


lab =

  1×4 cell array

    {'co'}    {'uděláš'}    {'{pause}'}    {'nejdřív'}

Vectorized operations

labelsOfInterest = {'i', 'i:', 'e', 'e:', 'a', 'a:', 'o', 'o:', 'u', 'u:'};
tierInd = tgI(tg, 'phone');
condition = ismember( tg.tier{tierInd}.Label, labelsOfInterest );

count = sum(condition)
dur = tg.tier{tierInd}.T2(condition) - tg.tier{tierInd}.T1(condition);
meanDur = mean( dur )

figure, hist(dur)
count =

    19


meanDur =

    0.0632

Cut TextGrid

tg = tgRead('demo/H.TextGrid');
figure
tgPlot(tgCut(tg, 2, 2.2)); % preserve times
figure
tgPlot(tgCut0(tg, 2, 2.2)); % time from 0

Find labels in TextGrid: find label 'n' in phoneme tier (Point tier)

tg = tgRead('demo/H_plain.TextGrid');
i = tgFindLabels(tg, 'phoneme', 'n')   % get result indices
length(i)  % 4 results
i{1}       % index of the first result
tg.tier{tgI(tg, 'phoneme')}.Label{i{1}}   % label of the 1st result
tg.tier{tgI(tg, 'phoneme')}.Label(cell2mat(i))  % all found label: cell-array of char strings
i =

  1×4 cell array

    {[8]}    {[18]}    {[25]}    {[42]}


ans =

     4


ans =

     8


ans =

    'n'


ans =

  1×4 cell array

    {'n'}    {'n'}    {'n'}    {'n'}

Find labels in TextGrid: find fragments with successive labels '?' 'a' in phone tear (Interval tier)

i = tgFindLabels(tg, 'phone', {'?', 'a'})
length(i)   % 2 results found
i{1}        % indices of the first result
length(i{1}) % length of the first result
i{1}(2)     % index of the 2nd label of the first result
tg.tier{tgI(tg, 'phone')}.Label{i{1}(2)}  % 2nd label of the first result
i{2}        % indices of the second result
tg.tier{tgI(tg, 'phone')}.Label(i{1})  % label of the 1st result: cell-array of char strings
tg.tier{tgI(tg, 'phone')}.Label(i{2})  % 2nd result
tg.tier{tgI(tg, 'phone')}.Label(cell2mat(i))  % all results together: cell-array of char strings
i =

  1×2 cell array

    {1×2 double}    {1×2 double}


ans =

     2


ans =

    39    40


ans =

     2


ans =

    40


ans =

    'a'


ans =

    41    42


ans =

  1×2 cell array

    {'?'}    {'a'}


ans =

  1×2 cell array

    {'?'}    {'a'}


ans =

  1×4 cell array

    {'?'}    {'a'}    {'?'}    {'a'}

Find labels in TextGrid: find fragments with successive labels '?' 'a' in phone tier and return initial and final time of these fragments

t = tgFindLabels(tg, 'phone', {'?', 'a'}, true)  % 2 results and their initial (T1) and final (T2) time
t.T2(1) - t.T1(1)    % duration of the first fragment
t.T2(2) - t.T1(2)    % duration of the second fragment
t.T2 - t.T1          % durations of all fragments
t = 

  struct with fields:

    T1: [2.9114 3.0236]
    T2: [3.0236 3.1063]


ans =

    0.1122


ans =

    0.0827


ans =

    0.1122    0.0827

Find labels in TextGrid: find fragments with successive labels 'ti' 'reknu' 'co' in word tier (Interval tier)

i = tgFindLabels(tg, 'word', {'ti', 'reknu', 'co'})
length(i)   % 1 result found
i{1}          % indices of segments in the first (and only) result
length(i{1})  % length of the first (and only) result (number of segments)
i{1}(2)       % index of the second segment in the first result
tg.tier{tgI(tg, 'word')}.Label{i{1}(2)} % second label in the fragment
tg.tier{tgI(tg, 'word')}.Label(i{1})    % all labels in the fragment: cell-array of char strings
i =

  1×1 cell array

    {1×3 double}


ans =

     1


ans =

     3     4     5


ans =

     3


ans =

     4


ans =

    'reknu'


ans =

  1×3 cell array

    {'ti'}    {'reknu'}    {'co'}

Find labels in TextGrid: get initial and final time of the fragment and extract this interval from the PitchTier

t = tgFindLabels(tg, 'word', {'ti', 'reknu', 'co'}, true)
pt = ptRead('demo/H.PitchTier');
tStart = t.T1(1)
tEnd = t.T2(1)
figure, ptPlot(ptCut(pt, tStart, tEnd))
t = 

  struct with fields:

    T1: 0.2160
    T2: 0.7600


tStart =

    0.2160


tEnd =

    0.7600

Create syllable tier from phone tier

tg = tgRead('demo/H.TextGrid');
tg = tgRemoveTier(tg, 5); tg = tgRemoveTier(tg, 4); tg = tgRemoveTier(tg, 3); tg = tgRemoveTier(tg, 1); % get phone tier only

% Get actual labels - concatenated (collapsed into one string)
% Use horzcat instead of strcat because strcat removes leading white-space chars which is undesirable.
collapsed = horzcat(tg.tier{tgI(tg, 'phone')}.Label{:})

% Edit the collapsed string with labels - insert separators to mark boundaries of syllables.

% * There can be segments with empty labels in the original tier (pauses), do not specify them in the pattern.
% * Beware of labels that appear empty but they are not (space, new line character etc.) - these segments are handled as classical non-empty labels. See example - one label is ' ', therefore it must be specified in the pattern.

pattern = 'ja:-ci-P\ek-nu-t_so-?u-J\e-la:S- -nej-dP\i:f-naj-deZ-h\ut_S-ku-?a-?a-ta-ma-na:'
tg2 = tgDuplicateTierMergeSegments(tg, 'phone', 1, 'syll', pattern, '-');
figure, tgPlot(tg2);
collapsed =

    'ja:ciP\eknut_so?uJ\ela:S nejdP\i:fnajdeZh\ut_Sku?a?atamana:'


pattern =

    'ja:-ci-P\ek-nu-t_so-?u-J\e-la:S- -nej-dP\i:f-naj-deZ-h\ut_S-ku-?a-?a-ta-ma-na:'

Overview of some TextGrid operations

For all functions, see help for description and example of use.

tg = tgRead('demo/H.TextGrid');
figure, tgPlot(tg)

tg = tgRemoveTier(tg, 'syllable');
tg = tgRemoveTier(tg, 'phrase');
tg = tgRemoveTier(tg, 'phone');

ind = tgGetPointIndexNearestTime(tg, 'phoneme', 1.5);
tg = tgSetLabel(tg, 'phoneme', ind, '!Q!');
tg = tgInsertPoint(tg, 'phoneme', 1.6, 'NEW');
tg.tier{tgI(tg, 'phoneme')}.T(30: 40) = [];      % remove points
tg.tier{tgI(tg, 'phoneme')}.Label(30: 40) = [];

tg = tgDuplicateTier(tg, 'word', 2);
tg = tgSetTierName(tg, 2, 'WORD2');
tg = tgRemoveIntervalBothBoundaries(tg, 'WORD2', 6);
tg = tgSetLabel(tg, 'WORD2', 5, '');
tg = tgInsertInterval(tg, 'WORD2', 0.9, 1.7, 'NEW LAB');
ind = tgGetIntervalIndexAtTime(tg, 'WORD2', 2.3);
tg = tgRemoveIntervalBothBoundaries(tg, 'WORD2', ind);
figure, tgPlot(tg)

tgNew = tgCreateNewTextGrid(0, 5);
tgNew = tgInsertNewIntervalTier(tgNew, 1, 'word');   % the first tier
tgNew = tgInsertInterval(tgNew, 1, 2, 3.5, 'hello');
tgNew = tgInsertInterval(tgNew, 1, 4, 4.8, 'world');
tgNew = tgInsertNewIntervalTier(tgNew, Inf, 'wordLast');  % the last tier (at this moment)
tgNew = tgInsertInterval(tgNew, 'wordLast', 2, 3, 'ABC');
tgNew = tgInsertNewPointTier(tgNew, 2, 'click');
tgNew = tgInsertPoint(tgNew, 2, 2, 'click');
tgNew = tgInsertPoint(tgNew, 2, 4, 'click');
tgNew = tgInsertNewPointTier(tgNew, Inf, 'pointTierLast');
tgNew = tgInsertPoint(tgNew, 'pointTierLast', 3, 'point');
figure, tgPlot(tgNew)
tgWrite(tgNew, 'demo/ex_output.TextGrid');
Warning: TextGrid has a duplicate tier name [word]. You should not use the name
for indexing to avoid ambiguity. 

Repair continuity problem of TextGrid

Repairs problem of continuity of T2 and T1 in interval tiers. This problem is very rare and it should not appear. However, e.g., automatic segmentation tool Prague Labeller produces random numeric round-up errors featuring, e.g., T2 of preceding interval is slightly higher than the T1 of the current interval. Because of that, the boundary cannot be manually moved in Praat edit window.

tgProblem = tgRead('demo/H_problem.TextGrid');
tgNew = tgRepairContinuity(tgProblem);
tgWrite(tgNew, 'demo/H_problem_OK.TextGrid');

tgNew2 = tgRepairContinuity(tgNew); % no problem in repaired TextGrid
Problem found [tier: 2, int: 16, 17] t2 = 0.951000010000, t1 = 0.951000000000. New value: 0.951000005000.
Problem found [tier: 2, int: 24, 25] t2 = 2.048900000010, t1 = 2.048900000000. New value: 2.048900000005.
Problem found [tier: 4, int: 2, 3] t2 = 0.215900000000, t1 = 0.215899900000. New value: 0.215899950000.
Problem found [tier: 4, int: 5, 6] t2 = 0.760000000000, t1 = 0.760100000000. New value: 0.760050000000.

PitchTier

Transform Hz to semitones (ST), cut the original pitchtier along the TextGrid, make interpolated contour.

pt = ptRead('demo/H.PitchTier');
pt = ptHz2ST(pt, 100);  % conversion of Hz to Semitones, reference 0 ST = 100 Hz.

figure
subplot(3,1,1)
ptPlot(pt); xlabel('Time (sec)'); ylabel('Frequency (ST)');

subplot(3,1,2)
tg = tgRead('demo/H.TextGrid');
labelsOfInterest = tg.tier{tgI(tg, 'word')}.Label(2:6) .'

pt2 = ptCut0(pt, tgGetIntervalStartTime(tg, 'word', 2), tgGetIntervalEndTime(tg, 'word', 6));
ptPlot(pt2); xlabel('Time (sec)'); ylabel('Frequency (ST)');

subplot(3,1,3)
pt2interp = ptInterpolate(pt2, pt2.t(1): 0.001: pt2.t(end));
ptPlot(pt2interp); xlabel('Time (sec)'); ylabel('Frequency (ST)');

ptWrite(pt2interp, 'demo/H_cut_interp.PitchTier')
labelsOfInterest =

  5×1 cell array

    {'já'    }
    {'ti'    }
    {'řeknu' }
    {'co'    }
    {'uděláš'}

Legendre polynomials modelling

% Orthogonal basis
figure
ptLegendreDemo()

% Cut the PitchTier
pt = ptRead('demo/H.PitchTier');
pt = ptHz2ST(pt, 100);
pt = ptCut(pt, 3);  % cut PitchTier from t = 3 sec and preserve time

% Model it using Legendre polynomials
c = ptLegendre(pt)

% Reconstruct the contour from these 4 coefficients
leg = ptLegendreSynth(c);
ptLeg = pt;
ptLeg.t = linspace(ptLeg.tmin, ptLeg.tmax, length(leg));
ptLeg.f = leg;

figure
plot(pt.t, pt.f, 'ko'); xlabel('Time (sec)'); ylabel('F0 (ST re 100 Hz)');
hold on
plot(ptLeg.t, ptLeg.f, 'b')
hold off
axis tight
c =

   10.2782   -2.8105   -0.3033    1.7146

Pitch object

In addition to PitchTier, a Pitch object represents periodicity candidates as a function of time.

p = pitchRead('demo/sound.Pitch');
p
p.t(4)      % time instance of the 4th frame
p.frame{4}  % 4th frame: pitch candidates
p.frame{4}.frequency(2)
p.frame{4}.strength(2)
p = 

  struct with fields:

              xmin: 0
              xmax: 5.1120
                nx: 508
                dx: 0.0100
                x1: 0.0210
                 t: [1×508 double]
           ceiling: 600
    maxnCandidates: 15
             frame: {1×508 cell}


ans =

    0.0510


ans = 

  struct with fields:

      intensity: 6.3594e-05
    nCandidates: 4
      frequency: [0 6.2524e+03 3.3928e+03 1.1971e+03]
       strength: [0 0.3169 0.2917 0.2759]


ans =

   6.2524e+03


ans =

    0.3169

Formant object

A Formant object represents formant frequencies and bandwidths as a function of time.

f = formantRead('demo/maminka.Formant');
f
f.t(4)      % time instance of the 4th frame
f.frame{4}  % 4th frame: formants
f.frame{4}.frequency(2)
f.frame{4}.bandwidth(2)
f = 

  struct with fields:

            xmin: 0
            xmax: 0.5461
              nx: 80
              dx: 0.0063
              x1: 0.0262
               t: [1×80 double]
    maxnFormants: 5
           frame: {1×80 cell}


ans =

    0.0449


ans = 

  struct with fields:

    intensity: 1.0331e-05
    nFormants: 5
    frequency: [192.4870 1.4792e+03 2.8833e+03 3.9694e+03 5.2315e+03]
    bandwidth: [234.8467 295.1070 160.2312 452.2355 1.2429e+03]


ans =

   1.4792e+03


ans =

  295.1070

IntensityTier

Intensity tier files have very similar structure to PitchTiers. Using this toolbox, you can also interpolate, cut or model Intensity tiers with Legendre polynomials.

it = itRead('demo/maminka.IntensityTier');
it = itCut(it, 0.2, 0.4);  % cut IntensityTier and preserve time
c = itLegendre(it)
leg = itLegendreSynth(c);
itLeg = it;
itLeg.t = linspace(itLeg.tmin, itLeg.tmax, length(leg));
itLeg.i = leg;
figure, plot(it.t, it.i, 'ko')
xlabel('Time (sec)'); ylabel('Intensity (dB)')
hold on; plot(itLeg.t, itLeg.i, 'b')
c =

   68.6361   -5.3558   -4.6508   -4.9473

Read Collections of objects

With col.read() function, it is convenient to read a lot of objects stored in one Praat .Collection file

coll = colRead('demo/coll_text.Collection');
length(coll)  % number of objects in collection
coll{1}.type  % 1st object type
coll{1}.name  % 1st object name
it = coll{1}; % 1st object
figure, itPlot(it)

coll{2}.type  % 2nd object type
coll{2}.name  % 2nd object name
tg = coll{2}; % 2nd object
figure, tgPlot(tg)
length(tg.tier)  % number of tiers in TextGrid
tg.tier{tgI(tg, 'word')}.Label

coll{3}.type  % 3rd object type
coll{3}.name  % 3rd object name
pitch = coll{3}; % 3rd object
pitch.nx         % number of frames
pitch.t(4)       % time instance of the 4th frame
pitch.frame{4}   % th frame: pitch candidates
pitch.frame{4}.frequency(2)
pitch.frame{4}.strength(2)

coll{4}.type  % 4th object type
coll{4}.name  % 4th object name
pt = coll{4}; % 4th object
figure, ptPlot(pt)
ans =

     5


ans =

    'IntensityTier'


ans =

    '1'


ans =

    'TextGrid'


ans =

    'HC101bA'


ans =

     4


ans =

  1×19 cell array

  Columns 1 through 7

    {'#SIL#'}    {'i'}    {'na'}    {'tom'}    {'#SP#'}    {'že'}    {'člověk'}

  Columns 8 through 14

    {'si'}    {'opatří'}    {'psa'}    {'?'}    {'aby'}    {'nebyl'}    {'sám'}

  Columns 15 through 19

    {'?'}    {'je'}    {'mnoho'}    {'pravdy'}    {'#SIL#'}


ans =

    'Pitch 1'


ans =

    'sound_short'


ans =

   508


ans =

    0.0510


ans = 

  struct with fields:

      intensity: 6.3594e-05
    nCandidates: 4
      frequency: [0 6.2524e+03 3.3928e+03 1.1971e+03]
       strength: [0 0.3169 0.2917 0.2759]


ans =

   6.2524e+03


ans =

    0.3169


ans =

    'PitchTier'


ans =

    'H_shortTextFile'

Process all files in folder

inputFolder = 'experiment1/data';
listFiles = dir([inputFolder '/*.TextGrid']);  % Beware of case-sensitive file system!
for (I = 1: length(listFiles))
    file = listFiles(I).name;
    if listFiles(I).isdir
        continue
    end
    fileName = file(1:end-9);
    fileTextGrid = [inputFolder, '/', fileName, '.TextGrid'];
    filePitchTier = [inputFolder, '/', fileName, '.PitchTier'];
    fileIntensityTier = [inputFolder, '/', fileName, '.IntensityTier'];
    fileSound = [inputFolder, '/', fileName, '.wav'];
    %
    tg = tgRead(fileTextGrid);
    pt = ptRead(filePitchTier);
    % your code %
end