mPraat demonstration
Contents
- mPraat and rPraat homepage
- How to cite this toolbox?
- Installation and help
- Read and plot TextGrid, PitchTier and Sound
- TextGrid
- Tier accessed both by index and name (TextGrid)
- Point tier (TextGrid)
- Point tier 'low-level access' (TextGrid)
- Interval tier (TextGrid)
- Interval tier 'low-level access' (TextGrid)
- Vectorized operations
- Cut TextGrid
- Find labels in TextGrid: find label 'n' in phoneme tier (Point tier)
- Find labels in TextGrid: find fragments with successive labels '?' 'a' in phone tear (Interval tier)
- Find labels in TextGrid: find fragments with successive labels '?' 'a' in phone tier and return initial and final time of these fragments
- Find labels in TextGrid: find fragments with successive labels 'ti' 'reknu' 'co' in word tier (Interval tier)
- Find labels in TextGrid: get initial and final time of the fragment and extract this interval from the PitchTier
- Create syllable tier from phone tier
- Overview of some TextGrid operations
- Repair continuity problem of TextGrid
- PitchTier
- Legendre polynomials modelling
- Pitch object
- Formant object
- IntensityTier
- Read Collections of objects
- Process all files in folder
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