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
The final publication is available at Springer via DOI 10.1007/978-3-319-45510-5_42
Download BibTeX reference mpraatrpraat2016.bib
rPraat and mPraat homepage: https://fu.ff.cuni.cz/praat/
CRAN link: https://CRAN.R-project.org/package=rPraat
Package rPraat at github: https://github.com/bbTomas/rPraat/
install.packages("rPraat")
At each R session, you have to initialize the package.
library(rPraat) library(dplyr) # optional, if you want to use the pipeline operator %>%
For help and examples, use command
?nameOfFunction
or read .pdf documentation at https://github.com/bbTomas/rPraat/blob/master/rPraat.pdf
To reproduce the following examples, you will need some sample files. Download and extract rpraat_demo.zip to your Working Directory, it creates demo
subdirectory.
Mouse drag: Zoom to selection
Mouse Double-click: Default zoom
Shift + mouse drag: plot scroll
library(rPraat) library(dplyr, warn.conflicts = FALSE) library(dygraphs)
library(rPraat) library(dplyr) library(dygraphs)
sndWav <- snd.read("demo/H.wav") snd.plot(sndWav, group = "my_group1") %>% dyAxis("y", label = "Amplitude")
tg <- tg.read("demo/H.TextGrid") %>% tg.removeTier("phrase") %>% tg.removeTier("phoneme") %>% tg.removeTier("syllable") tg.plot(tg, group="my_group1")
pt <- pt.read("demo/H.PitchTier") pt.plot(pt, group="my_group1") %>% dyAxis("y", label = "Frequency (Hz)")
it <- it.read("demo/H.IntensityTier") it.plot(it, group="my_group1") %>% dyAxis("y", label = "Intensity (dB)")
formant <- formant.read("demo/H.Formant") formant.plot(formant, group="my_group1") %>% dyAxis("y", label = "Frequency (Hz)")
pitch <- pitch.read("demo/H.Pitch") pitch.plot(pitch, group="my_group1", scaleIntensity = TRUE, showStrength = TRUE) %>% dyAxis("y", label = "Frequency (Hz)")
tg.plot(tg.sample(), pt = pt.sample(), it = it.sample()) tg.plot(tg.sample(), formant = formant.sample()) tg.plot(tg.sample(), pitch = pitch.sample(), pitchShowStrength = TRUE, pt = pt.sample())
snd <- snd.read("demo/H.wav") snd.plot(snd)
snd <- snd.read("demo/H.wav")
snd.write(snd, "demo/temp.wav")
snd$fs snd$nChannels snd$nBits snd$nSamples snd$duration head(snd$t) head(snd$sig)
snd <- snd.read("demo/cali.wav") snd.plot(snd)
snd <- snd.read("demo/cali.wav")
snd$nChannels head(snd$t) head(snd$sig)
fs <- 48000 # Hz duration <- 0.005 # sec f <- 1000 # Hz t <- seq(0, duration*fs-1)/fs sig <- 1*sin(2*pi*f*t) snd <- list(sig = sig, fs = fs) snd.plot(snd)
fs <- 48000 # Hz duration <- 0.005 # sec f <- 1000 # Hz t <- seq(0, duration*fs-1)/fs sig <- 1*sin(2*pi*f*t) snd <- list(sig = sig, fs = fs)
snd.plot(snd, stemPlot = TRUE)
snd$nBits = 16 snd.write(snd, "demo/temp.wav") snd <- snd.read("demo/temp.wav") min(snd$sig) max(snd$sig) max(snd$sig)-1 min(snd$sig)+1 mean(snd$sig)
snd <- snd.sample() snd2 <- snd.cut(snd, Start = 0.2, End = 0.3) snd3 <- snd.cut0(snd, Start = 2000, End = 3000, units = "samples")
snd <- snd.sample() snd.plot(snd)
snd2 <- snd.cut(snd, Start = 0.2, End = 0.3) snd.plot(snd2)
snd3 <- snd.cut0(snd, Start = 2000, End = 3000, units = "samples") snd.plot(snd3)
tg <- tg.read("demo/H.TextGrid")
tg <- tg.read("demo/H.TextGrid") tg.plot(tg)
tg.plot(tg[c("phone", "word")]) # Plot selected tiers only tg.write(tg, "demo/new_file.TextGrid") # save variable into a new file
Align boundaries of intervals in the "word" tier according to closest boundaries in the "phone" tier. If there is no boundary within the tolerance limit in the pattern tier, the boundary position in the target tier is kept at its original position.
tg <- tg.read("demo/H_shifted.TextGrid") tg.plot(tg) tg2 <- tg.boundaryMagnet(tg, targetTier = "word", patternTier = "phone") tg.plot(tg2)
tg <- tg.read("demo/H.TextGrid") tg.getNumberOfTiers(tg) length(tg) # get number of tiers, "low-level access" tg.getTotalDuration(tg) tg.getStartTime(tg) tg.getEndTime(tg) tg.getTierName(tg, 1) # get name of the first tier
tg.isPointTier(tg, 1) tg.isPointTier(tg, "phoneme") tg.isIntervalTier(tg, 1) tg.isIntervalTier(tg, "phoneme") tg[[1]]$type tg$phoneme$type tg.checkTierInd(tg, "word") # get index of a tier with specified name tg.checkTierInd(tg, 4) # only checks whether TextGrid contains a tier with this index
tg.getNumberOfPoints(tg, 1) tg.getNumberOfPoints(tg, "phoneme") tg.getPointTime(tg, "phoneme", 4) tg.getLabel(tg, "phoneme", 4)
length(tg[[1]]$t) # number of points tg[[1]]$t[4] tg[[1]]$label[4] length(tg$phoneme$t) tg$phoneme$t[4] tg$phoneme$label[4] tg$phoneme$t[5:8]
tg.isPointTier(tg, "word") tg.isIntervalTier(tg, "word") tg[[4]]$type tg$word$type tg.getTotalDuration(tg, "word") tg.getStartTime(tg, "word") tg.getEndTime(tg, "word") tg.getNumberOfIntervals(tg, "word") tg.getIntervalStartTime(tg, "word", index = 4) tg.getIntervalEndTime(tg, "word", index = 4) tg.getIntervalDuration(tg, "word", index = 4) tg.getLabel(tg, "word", index = 4)
length(tg[[4]]$t1) # number of intervals tg[[4]]$t1[4] tg[[4]]$t2[4] tg[[4]]$label[4] length(tg$word$t1) tg$word$t1[4] tg$word$t2[4] tg$word$label[4] tg$word$label[5:8]
labelsOfInterest <- c("i", "i:", "e", "e:", "a", "a:", "o", "o:", "u", "u:") condition <- tg$phone$label %in% labelsOfInterest sum(condition) # How many labels satisfy the condition? dur <- tg$phone$t2[condition] - tg$phone$t1[condition] mean(dur) hist(dur, breaks = 10) # let's put them into a table tab <- data.frame(Vowel = tg$phone$label[condition], Duration = tg$phone$t2[condition] - tg$phone$t1[condition]) library(dplyr) tab %>% group_by(Vowel) %>% summarise(DurMean = mean(Duration))
tg.plot(tg.cut(tg.sample(), 2, 2.2)) # preserve times
tg.plot(tg.cut0(tg.sample(), 2, 2.2)) # time from 0
tg <- tg.read("demo/H.TextGrid") # find label "n" in phoneme tier (Point tier) i <- tg.findLabels(tg, "phoneme", "n") i # four results and their indices length(i) # how many results? i[[1]] # index of the first result tg$phoneme$label[unlist(i)] # find fragments with successive labels "?" "a" in 'phone' tear (Interval tier) i <- tg.findLabels(tg, "phone", c("?", "a")) i length(i) # 2 results found i[[1]] # indices of the first result i[[2]] # indices of the second result tg$phone$label[i[[1]]] tg$phone$label[i[[2]]] tg$phone$label[unlist(i)] # find fragments with successive labels "?" "a" in 'phone' tier # and return initial and final time of these fragments t <- tg.findLabels(tg, "phone", c("?", "a"), returnTime = TRUE) t # 2 results and their initial (t1) and final (t2) time length(t$t1) # 2 results found (Note: length(t$t1) == length(t$t2) == number of results) t$t2[1] - t$t1[1] # duration of the first fragment t$t2[2] - t$t1[2] # duration of the second fragment # find fragments with successive labels "ti" "řeknu" "co" in 'word' tier (Interval tier) i <- tg.findLabels(tg, "word", c("ti", "řeknu", "co")) i length(i) # 1 result found i[[1]] # indices of segments in the first result length(i[[1]]) # length of the first (and only) result (number of segments) i[[1]][3] # index of the third segment in the first result tg$word$label[i[[1]]] # all labels in the fragment # get initial and final time of the fragment t <- tg.findLabels(tg, "word", c("ti", "řeknu", "co"), returnTime = TRUE) t
tg <- tg.read("demo/H.TextGrid")
Get actual labels of 'phone' tier.
collapsed <- paste0(tg$phone$label, collapse = "") collapsed
Edit the collapsed string with labels - insert separators to mark boundaries of syllables.
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 <- tg.duplicateTierMergeSegments(tg, "phone", 1, "syll", pattern, sep = "-") tg.plot(tg2[c("syll", "phone")])
For all functions, see help ?functionName
for its description and example of use.
tg <- tg.read("demo/H.TextGrid") tg.plot(tg)
tg <- tg.removeTier(tg, "syllable") tg <- tg.removeTier(tg, "phrase") tg <- tg.removeTier(tg, "phone") ind <- tg.getPointIndexNearestTime(tg, "phoneme", time = 1.5) tg <- tg.setLabel(tg, "phoneme", ind, newLabel = "!Q!") tg <- tg.insertPoint(tg, "phoneme", time = 1.6, label = "NEW") tg$phoneme$t <- tg$phoneme$t[-(30:40)] # remove points 30:40 tg$phoneme$label <- tg$phoneme$label[-(30:40)] tg <- tg.duplicateTier(tg, "word", newInd = 2, newTierName = "WORD2") tg <- tg.removeIntervalBothBoundaries(tg, "WORD2", index = 6) tg <- tg.setLabel(tg, "WORD2", index = 5, newLabel = "") tg <- tg.insertInterval(tg, "WORD2", tStart = 0.9, tEnd = 1.7, label = "NEW LAB") ind <- tg.getIntervalIndexAtTime(tg, "WORD2", time = 2.3) tg <- tg.removeIntervalBothBoundaries(tg, "WORD2", ind) tg.plot(tg)
tgNew <- tg.createNewTextGrid(tMin = 0, tMax = 5) tgNew <- tg.insertNewIntervalTier(tgNew, newInd = 1, "word") # the first tier tgNew <- tg.insertInterval(tgNew, tierInd = 1, tStart = 2, tEnd = 3.5, "hello") tgNew <- tg.insertInterval(tgNew, tierInd = 1, 4, 4.8, "world") tgNew <- tg.insertNewIntervalTier(tgNew, newInd = Inf, "word_last") # the last tier (at this moment) tgNew <- tg.insertInterval(tgNew, tierInd = "word_last", 2, 3, "ABC") tgNew <- tg.insertNewPointTier(tgNew, newInd = 2, newTierName = "click") tgNew <- tg.insertPoint(tgNew, tierInd = 2, time = 2, label = "click") tgNew <- tg.insertPoint(tgNew, tierInd = 2, time = 4, label = "click") tgNew <- tg.insertNewPointTier(tgNew, newInd = Inf, newTierName = "pointTierLast") tgNew <- tg.insertPoint(tgNew, tierInd = "pointTierLast", time = 3, label = "point") tg.plot(tgNew) tg.write(tgNew, "demo/ex_output.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 <- tg.read("demo/H_problem.TextGrid") tgNew <- tg.repairContinuity(tgProblem) tg.write(tgNew, "demo/H_problem_OK.TextGrid") tgNew2 <- tg.repairContinuity(tgNew) # no problem in repaired TextGrid
Example. Transform Hz to semitones (ST), cut the original PitchTier along the TextGrid, make interpolated contour.
pt <- pt.read("demo/H.PitchTier") pt.plot(pt) %>% dygraphs::dyAxis("y", label = "Frequency (Hz)") pt.write(pt, "demo/new_file.PitchTier") # save variable into a new file
pt <- pt.Hz2ST(pt, ref = 100) # conversion of Hz to Semitones, reference 0 ST = 100 Hz.
pt <- pt.Hz2ST(pt, ref = 100) # conversion of Hz to Semitones, reference 0 ST = 100 Hz. pt.plot(pt) %>% dygraphs::dyAxis("y", label = "Frequency (ST re 100 Hz)")
tg <- tg.read("demo/H.TextGrid")
tg <- tg.read("demo/H.TextGrid") tg.plot(tg["word"], pt = pt) # plot "word" tier only together with PitchTier
t <- tg.findLabels(tg, "word", c("já", "ti", "řeknu"), returnTime = TRUE) print(t)
pt2 <- pt.cut0(pt, tStart = t$t1[1], tEnd = t$t2[1]) # "já ti řeknu" pt.plot(pt2) %>% dygraphs::dyAxis("y", label = "Frequency (ST re 100 Hz)")
pt2 <- pt.cut0(pt, tStart = t$t1[1], tEnd = t$t2[1]) # "já ti řeknu"
pt2interp <- pt.interpolate(pt2, seq(pt2$t[1], pt2$t[length(pt2$t)], by = 0.001)) pt.plot(pt2interp) %>% dygraphs::dyAxis("y", label = "Frequency (ST re 100 Hz)") pt.write(pt2interp, "demo/H_cut_interp.PitchTier")
pt.legendreDemo()
c <- pt.legendre(pt2) print(c)
leg <- pt.legendreSynth(c) ptLeg <- pt2 ptLeg$t <- seq(ptLeg$tmin, ptLeg$tmax, length.out = length(leg)) ptLeg$f <- leg plot(pt2$t, pt2$f, xlab = "Time (sec)", ylab = "F0 (ST re 100 Hz)") lines(ptLeg$t, ptLeg$f, col = "blue")
In addition to PitchTier, a Pitch object represents periodicity candidates as a function of time.
p <- pitch.read("demo/H.Pitch") names(p) p$nx # get number of frames p$t[5] # time instance of the 5th frame p$frame[[5]] # 5th frame: pitch candidates p$frame[[5]]$frequency[2] p$frame[[5]]$strength[2]
pitch.write(p, "demo/temp.Pitch") pitch.plot(p, scaleIntensity = TRUE, showStrength = TRUE)
pitchArray <- pitch.toArray(pitch.sample()) pitchArray$frequencyArray[, 1:7] pitch <- pitch.toFrame(pitchArray) pitch$frame[[1]]
pitch.plot(pitch.sample(), pt = pt.sample(), showStrength = TRUE)
pitch <- pitch.sample() %>% pitch.cut(tStart = 0.7, tEnd = 1.5) pitch.plot(pitch, showStrength = TRUE)
A Formant object represents formant frequencies and bandwidths as a function of time.
f <- formant.read("demo/maminka.Formant") names(f) f$nx # get number of frames 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]
formant.write(f, "demo/temp.Formant") formant.plot(f, scaleIntensity = TRUE, drawBandwidth = TRUE)
fArray <- formant.toArray(f) plot(fArray$t, fArray$frequencyArray[1, ], main = "1st Formant (Hz)") formant <- formant.toFrame(fArray)
tg.plot(tg.sample(), formant = formant.sample())
formant <- formant.sample() %>% formant.cut(tStart = 1, tEnd = 1.5) formant.plot(formant)
Intensity tier files have very similar structure to PitchTiers. Using this package, you can also interpolate, cut or model Intensity tiers with Legendre polynomials.
it <- it.read('demo/maminka.IntensityTier')
it <- it.read('demo/maminka.IntensityTier') it.plot(it) %>% dygraphs::dyAxis("y", label = "Intensity (dB)") it.write(it, "demo/new_file.IntensityTier") # save variable into a new file
it <- it.cut(it, tStart = 0.2, tEnd = 0.4) # cut IntensityTier from t = 0.2 to 0.4 sec and preserve time
c <- it.legendre(it) print(c) leg <- it.legendreSynth(c) itLeg <- it itLeg$t <- seq(itLeg$tmin, itLeg$tmax, length.out = length(leg)) itLeg$i <- leg plot(it$t, it$i, xlab = "Time (sec)", ylab = "Intensity (dB)") lines(itLeg$t, itLeg$i, col = "blue")
With col.read() function, it is convenient to read a lot of objects stored in one .Collection file.
coll <- col.read("demo/coll_text.Collection") length(coll) # number of objects in collection
class(coll[[1]])["type"] # 1st object type class(coll[[1]])["name"] # 1st object name
it <- coll[[1]] # 1st object it.plot(it)
class(coll[[2]])["type"] # 2nd object type class(coll[[2]])["name"] # 2nd object name tg <- coll[[2]] # 2nd object length(tg) # number of tiers in TextGrid tg$word$label
tg.plot(tg)
class(coll[[3]])["type"] # 3rd object type class(coll[[3]])["name"] # 3rd object type pitch <- coll[[3]] # 3rd object names(pitch) pitch$nx # number of frames pitch$t[4] # time instance of the 4th frame pitch$frame[[4]] # 4th frame: pitch candidates pitch$frame[[4]]$frequency[2] pitch$frame[[4]]$strength[2]
pitch.plot(pitch, showStrength = TRUE)
class(coll[[4]])["type"] # 4th object type class(coll[[4]])["name"] # 4th object name
pt <- coll[[4]] # 2nd object pt.plot(pt)
class(coll[[5]])["type"] # 5th object type class(coll[[5]])["name"] # 5th object type formant <- coll[[5]] # 5th object names(formant) formant$nx # number of frames formant$t[4] # time instance of the 4th frame formant$frame[[4]] # 4th frame: formants formant$frame[[4]]$frequency[2] formant$frame[[4]]$bandwidth[2]
formant.plot(formant)
col <- list(as.tg(tg.sample(), "My textgrid"), as.pt(pt.sample(), "My PitchTier 1"), as.pt(pt.Hz2ST(pt.sample()), "My PitchTier 2"), as.it(it.sample(), "My IntensityTier"), as.pitch(pitch.sample(), "My Pitch"), as.formant(formant.sample(), "My Formant")) col.write(col, "demo/temp.Collection")
```{R, eval = FALSE} inputFolder <- "experiment1/data" listFiles <- list.files(path = inputFolder, pattern = "\.TextGrid$" , ignore.case = TRUE)
for (I in seq_along(listFiles)) { file <- listFiles[I] fileName <- substr(file, 1, nchar(file) - nchar(".TextGrid"))
fileTextGrid <- paste0(inputFolder, "/", fileName, ".TextGrid") # filePitchTier <- paste0(inputFolder, "/", fileName, ".PitchTier") # filePitch <- paste0(inputFolder, "/", fileName, ".Pitch") # fileIntensityTier <- paste0(inputFolder, "/", fileName, ".IntensityTier") # fileFormant <- paste0(inputFolder, "/", fileName, ".Formant") # fileSound <- paste0(inputFolder, "/", fileName, ".wav") # fileCollection <- paste0(inputFolder, "/", fileName, ".Collection") tg <- tg.read(fileTextGrid) # pt <- pt.read(filePitchTier) # pitch <- pt.read(filePitch) # it <- it.read(fileIntensityTier) # formant <- formant.read(fileFormant) # snd <- snd.read(fileSound) # col <- col.read(fileCollection) # ... # ... process tg, pt, pitch, it, formant, snd, col...
}
## Descriptive statistics of all PitchTiers in folder ```{R, eval = FALSE} library(rPraat) inputFolder <- "experiment1/data" listFiles <- list.files(path = inputFolder, pattern = "\\.PitchTier$" , ignore.case = TRUE) tab <- data.frame(file = listFiles) # table with file names tab$median <- NA tab$mean <- NA tab$q10 <- NA tab$q90 <- NA tab$min <- NA tab$max <- NA tab$sd <- NA for (I in seq_along(listFiles)) { file <- listFiles[I] filePitchTier <- paste0(inputFolder, "/", file) pt <- pt.read(filePitchTier) pt <- pt.Hz2ST(pt) tab$median[I] <- median(pt$f) tab$mean[I] <- mean(pt$f) tab$q10[I] <- quantile(pt$f, 0.1) tab$q90[I] <- quantile(pt$f, 0.9) tab$min[I] <- min(pt$f) tab$max[I] <- max(pt$f) tab$sd[I] <- sd(pt$f) } plot(tab$median, tab$mean, main = cor(tab$median, tab$mean)) plot(tab$q90 - tab$q10, tab$sd, main = cor(tab$q90 - tab$q10, tab$sd)) plot(tab$max - tab$min, tab$sd, main = cor(tab$max - tab$min, tab$sd)) plot(tab$max - tab$min, tab$q90 - tab$q10, main = cor(tab$max - tab$min, tab$q90 - tab$q10)) plot(tab$median, tab$q90 - tab$q10, main = cor(tab$median, tab$q90 - tab$q10)) # save results library(writexl) write_xlsx(tab, "table.xlsx")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.