This vignette demonstrates how to perform integrated phonetic analysis workflows using the pladdrr package. We will cover:
This workflow is typical in phonetic research, where annotated speech data requires systematic acoustic analysis.
TextGrids are Praat’s standard format for time-aligned annotations. They consist of:
TextGrids enable:
A typical phonetic analysis workflow:
The pladdrr package provides all necessary tools for steps 2-5.
For demonstration, we create a 5-second audio signal. In real research, you would load existing recordings.
library(pladdrr)
# Create synthetic audio (440 Hz tone)
sound <- Sound$create_tone(
duration = 5.0,
frequency = 440,
sampling_rate = 16000,
amplitude = 0.5
)
# Inspect audio properties
cat("Duration:", sound$get_duration(), "seconds\n")
#> Duration: 5 seconds
cat("Sample rate:", sound$get_sampling_frequency(), "Hz\n")
#> Sample rate: 16000 Hz
cat("Channels:", sound$get_number_of_channels(), "\n")
#> Channels: 1Create a TextGrid with word and phone annotations:
# Create TextGrid with 2 interval tiers
tg <- TextGrid$create(
tmin = 0,
tmax = 5,
tier_names = "words phones",
point_tiers = "" # Both are interval tiers
)
# Add word boundaries
tg$insert_boundary(1, 1.0)
tg$insert_boundary(1, 2.5)
tg$insert_boundary(1, 4.0)
# Label word intervals
tg$set_interval_text(1, 1, "silence")
tg$set_interval_text(1, 2, "hello")
tg$set_interval_text(1, 3, "world")
tg$set_interval_text(1, 4, "silence")
# Add phone boundaries (finer granularity)
phone_times <- c(1.0, 1.3, 1.7, 2.0, 2.5, 3.0, 3.5, 4.0)
for (t in phone_times) {
tg$insert_boundary(2, t)
}
# Label phone intervals
phone_labels <- c("", "h", "E", "l", "oU", "w", "3", "ld", "")
for (i in seq_along(phone_labels)) {
tg$set_interval_text(2, i, phone_labels[i])
}
cat("TextGrid created with", tg$get_number_of_tiers(), "tiers\n")
#> TextGrid created with 2 tiers# Get tier information
tier_names <- tg$get_tier_names()
for (i in 1:tg$get_number_of_tiers()) {
tier_type <- if (tg$tier_is_interval_tier(i)) "IntervalTier" else "PointTier"
n_items <- tg$get_number_of_intervals(i)
cat(sprintf("Tier %d: %s (%s) - %d items\n",
i, tier_names[i], tier_type, n_items))
}
#> Tier 1: words (IntervalTier) - 4 items
#> Tier 2: phones (IntervalTier) - 9 items# Get all intervals from the phones tier
phone_tier <- 2
n_intervals <- tg$get_number_of_intervals(phone_tier)
# Create a data frame with interval information
phone_data <- data.frame(
interval = 1:n_intervals,
start = numeric(n_intervals),
end = numeric(n_intervals),
duration = numeric(n_intervals),
label = character(n_intervals),
stringsAsFactors = FALSE
)
for (i in 1:n_intervals) {
phone_data$start[i] <- tg$get_interval_start_time(phone_tier, i)
phone_data$end[i] <- tg$get_interval_end_time(phone_tier, i)
phone_data$duration[i] <- phone_data$end[i] - phone_data$start[i]
phone_data$label[i] <- tg$get_interval_text(phone_tier, i)
}
print(phone_data)
#> interval start end duration label
#> 1 1 0.0 1.0 1.0
#> 2 2 1.0 1.3 0.3 h
#> 3 3 1.3 1.7 0.4 E
#> 4 4 1.7 2.0 0.3 l
#> 5 5 2.0 2.5 0.5 oU
#> 6 6 2.5 3.0 0.5 w
#> 7 7 3.0 3.5 0.5 3
#> 8 8 3.5 4.0 0.5 ld
#> 9 9 4.0 5.0 1.0Extract specific portions of audio based on TextGrid intervals:
# Extract the "hello" word (interval 2 of word tier)
hello_start <- tg$get_interval_start_time(1, 2)
hello_end <- tg$get_interval_end_time(1, 2)
hello_sound <- sound$extract_part(
from_time = hello_start,
to_time = hello_end,
preserve_times = FALSE
)
cat("Extracted 'hello':", hello_sound$get_total_duration(), "seconds\n")
#> Extracted 'hello': 1.5 secondsChange sampling rate for analysis or compatibility:
Normalize loudness across recordings:
# Extract pitch contour
pitch <- sound$to_pitch(
time_step = 0.01,
pitch_floor = 75,
pitch_ceiling = 600
)
# Get statistics
f0_mean <- pitch$get_mean(from_time = 0, to_time = 0, unit = "hertz")
f0_sd <- pitch$get_standard_deviation(from_time = 0, to_time = 0,
unit = "hertz")
cat(sprintf("F0: Mean = %.1f Hz, SD = %.1f Hz\n", f0_mean, f0_sd))
#> F0: Mean = 440.0 Hz, SD = 0.0 Hz# Extract formants using Burg's algorithm
formant <- sound$to_formant_burg(
time_step = 0.01,
max_formants = 5,
max_frequency = 5500,
window_length = 0.025,
pre_emphasis_from = 50
)
# Measure formants at specific time point (e.g., vowel midpoint)
vowel_time <- 1.5 # Middle of "E" vowel
f1 <- formant$get_value_at_time(formant_number = 1, time = vowel_time,
unit = "hertz")
f2 <- formant$get_value_at_time(formant_number = 2, time = vowel_time,
unit = "hertz")
f3 <- formant$get_value_at_time(formant_number = 3, time = vowel_time,
unit = "hertz")
cat(sprintf("Formants at %.2f s: F1 = %.0f Hz, F2 = %.0f Hz, F3 = %.0f Hz\n",
vowel_time, f1, f2, f3))
#> Formants at 1.50 s: F1 = 404 Hz, F2 = 443 Hz, F3 = 481 HzProcess all vowel segments systematically:
# Identify vowel intervals: labels containing an uppercase vowel letter
# (X-SAMPA tense-vowel convention, e.g. "E", "oU") or "3" (rhotic vowel, as in
# "world")
vowel_intervals <- grep("[AEIOUY3]", phone_data$label, ignore.case = FALSE)
# Extract acoustic features for each vowel
vowel_features <- data.frame(
phone = character(),
start = numeric(),
end = numeric(),
duration = numeric(),
f0_mean = numeric(),
f1 = numeric(),
f2 = numeric(),
f3 = numeric(),
intensity = numeric(),
hnr = numeric(),
stringsAsFactors = FALSE
)
for (idx in vowel_intervals) {
label <- phone_data$label[idx]
start <- phone_data$start[idx]
end <- phone_data$end[idx]
midpoint <- start + (end - start) / 2
# Extract features at vowel midpoint
f0 <- pitch$get_value_at_time(time = midpoint, unit = "hertz",
interpolate = TRUE)
f1 <- formant$get_value_at_time(1, midpoint, "hertz")
f2 <- formant$get_value_at_time(2, midpoint, "hertz")
f3 <- formant$get_value_at_time(3, midpoint, "hertz")
int <- intensity$get_value_at_time(midpoint, interpolation = "cubic")
hnr <- harmonicity$get_value_at_time(midpoint, interpolation = "linear")
vowel_features <- rbind(vowel_features, data.frame(
phone = label,
start = start,
end = end,
duration = end - start,
f0_mean = if (is.na(f0)) NA else f0,
f1 = f1,
f2 = f2,
f3 = f3,
intensity = int,
hnr = hnr
))
}
print(vowel_features)
#> phone start end duration f0_mean f1 f2 f3 intensity
#> 1 E 1.3 1.7 0.4 439.9998 404.4790 442.5576 480.6363 54.71009
#> 2 oU 2.0 2.5 0.5 439.9998 404.6389 442.7037 480.7712 54.71009
#> 3 3 3.0 3.5 0.5 439.9998 404.4781 442.5568 480.6356 54.71009
#> hnr
#> 1 80.08884
#> 2 80.08884
#> 3 80.08884pladdrr provides
autoplot( )/autolayer() methods (shown above
for TextGrid) plus plot() methods for Sound,
Pitch, Formant, Intensity, and other analysis objects. For vowel spaces,
formant trajectories, pitch/intensity contours, spectrograms, and voice
quality reports, see vignette("visualization").
Analyze vowel variation across speakers, dialects, or social groups:
# Load multiple speakers
speakers <- c("speaker1.wav", "speaker2.wav", "speaker3.wav")
textgrids <- c("speaker1.TextGrid", "speaker2.TextGrid", "speaker3.TextGrid")
# Process all speakers
all_data <- data.frame()
for (i in seq_along(speakers)) {
sound <- Sound$new(speakers[i])
tg <- TextGrid$new(textgrids[i])
# ... extract features ...
all_data <- rbind(all_data, features)
}
# Statistical analysis
model <- lm(f1 ~ vowel * speaker, data = all_data)Track vowel development in second language learners:
Multi-dimensional voice profiling:
# Extract voice quality metrics
pointprocess <- sound$to_point_process_periodic_cc(pitch_floor = 75,
pitch_ceiling = 300)
voice_profile <- data.frame(
f0_mean = pitch$get_mean(),
f0_sd = pitch$get_standard_deviation(),
jitter = pointprocess$get_jitter_local(),
shimmer = pointprocess$get_shimmer_local(sound),
hnr = harmonicity$get_mean(),
intensity_mean = intensity$get_mean()
)
# Add cepstral peak prominence smoothed (CPPS)
# CPPS is sensitive to parameter choice - see example for details
cepstrogram <- sound$to_powercepstrogram(
pitch_floor = 60,
time_step = 0.002
)
voice_profile$cpps <- cepstrogram$get_cpps(
subtract_tilt = FALSE, # Match Praat default
time_averaging_window = 0.001,
quefrency_averaging_window = 0.0005,
pitch_floor = 60,
pitch_ceiling = 330
)Note: CPPS values are sensitive to parameters. See
system.file( "examples", "10_cpps_analysis.R", package = "pladdrr")
for parameter effects and clinical interpretation.
Always pre-emphasize before formant analysis:
Choose parameters based on speaker characteristics:
# Adult male
pitch <- sound$to_pitch(pitch_floor = 75, pitch_ceiling = 300)
formant <- sound$to_formant_burg(max_frequency = 5000)
# Adult female
pitch <- sound$to_pitch(pitch_floor = 100, pitch_ceiling = 500)
formant <- sound$to_formant_burg(max_frequency = 5500)
# Child
pitch <- sound$to_pitch(pitch_floor = 200, pitch_ceiling = 700)
formant <- sound$to_formant_burg(max_frequency = 8000)Acoustic measurements may fail:
The pladdrr package enables:
Annotation workflows with TextGrid creation and manipulation
Sound processing (segmentation, resampling, filtering)
Multi-dimensional acoustic analysis (prosody, voice quality, spectral features)
R integration for statistical analysis and
visualization via data.frame output and `autoplot(
)/autolayer()` methods
For more examples, see:
vignette("vowel-space-analysis") - F1-F2 analysis and
normalizationvignette("textgrid-workflows") - Advanced TextGrid
manipulationinst/examples/ - Complete workflow scripts