--- title: "Integrated Phonetic Analysis with pladdrr" author: "pladdrr Package Authors" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Integrated Phonetic Analysis with pladdrr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) ``` ## Overview This vignette demonstrates how to perform integrated phonetic analysis workflows using the **pladdrr** package. We will cover: - **TextGrid-based segmentation**: Create and manipulate annotations - **Sound manipulation**: Extract, resample, and normalize audio - **Acoustic measurements**: F0, formants, intensity, and voice quality - **Batch processing**: Analyze multiple segments efficiently - **Data export**: Prepare data for statistical analysis in R This workflow is typical in phonetic research, where annotated speech data requires systematic acoustic analysis. ## Background ### TextGrid Annotation TextGrids are Praat's standard format for time-aligned annotations. They consist of: - **Interval tiers**: Segments with start/end times and labels (e.g., words, phones) - **Point tiers**: Time points with labels (e.g., landmarks, events) TextGrids enable: - Manual or automatic (forced alignment) annotation - Multi-level linguistic annotation (phones, syllables, words, utterances) - Integration with acoustic analysis ### The Phonetic Analysis Pipeline A typical phonetic analysis workflow: 1. **Record** and preprocess audio 2. **Annotate** with TextGrid (manual or via forced alignment) 3. **Segment** audio based on annotations 4. **Extract** acoustic features for each segment 5. **Analyze** patterns statistically The **pladdrr** package provides all necessary tools for steps 2-5. ## Part 1: Loading and Creating Data ### Creating Synthetic Audio For demonstration, we create a 5-second audio signal. In real research, you would load existing recordings. ```{r create_audio} 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") cat("Sample rate:", sound$get_sampling_frequency(), "Hz\n") cat("Channels:", sound$get_number_of_channels(), "\n") ``` ### Creating a TextGrid Create a TextGrid with word and phone annotations: ```{r create_textgrid} # 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") ``` ### Plotting the TextGrid `autoplot( )` and `autolayer()` methods let you compose TextGrid tier displays with ggplot2: ```{r plot_textgrid} p <- ggplot2::autoplot(tg) p2 <- p + ggplot2::autolayer(tg, tier = 2, color = "darkred") class(p2) ``` ## Part 2: TextGrid Queries ### Exploring Tier Structure ```{r explore_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)) } ``` ### Extracting Interval Information ```{r query_intervals} # 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) ``` ## Part 3: Sound Manipulation ### Extracting Audio Segments Extract specific portions of audio based on TextGrid intervals: ```{r extract_segments} # 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") ``` ### Resampling Change sampling rate for analysis or compatibility: ```{r resample} # Resample to 8 kHz (e.g., for telephony simulations) sound_8k <- sound$resample(new_frequency = 8000, precision = 50) cat("Resampled to", sound_8k$get_sampling_frequency(), "Hz\n") ``` ### Intensity Normalization Normalize loudness across recordings: ```{r normalize} # Scale to 70 dB SPL sound_normalized <- sound$scale_intensity(new_intensity = 70) cat("Normalized to 70 dB\n") ``` ### Pre-emphasis Filtering Apply pre-emphasis for formant analysis: ```{r preemphasis} # Pre-emphasize from 50 Hz sound_preemph <- sound$pre_emphasize(from_frequency = 50) cat("Applied pre-emphasis from 50 Hz\n") ``` ## Part 4: Acoustic Analysis ### Fundamental Frequency (F0) Extraction ```{r f0_extraction} # 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)) ``` ### Formant Analysis ```{r formant_analysis} # 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)) ``` ### Intensity ```{r intensity} # Extract intensity contour intensity <- sound$to_intensity( minimum_pitch = 100, time_step = 0.01 ) int_mean <- intensity$get_mean(from_time = 0, to_time = 0) cat(sprintf("Mean intensity: %.1f dB\n", int_mean)) ``` ### Voice Quality (HNR) ```{r hnr} # Harmonics-to-Noise Ratio harmonicity <- sound$to_harmonicity_cc( time_step = 0.01, min_pitch = 75, silence_threshold = 0.1, periods_per_window = 1.0 ) hnr_mean <- harmonicity$get_mean(from_time = 0, to_time = 0) cat(sprintf("Mean HNR: %.1f dB\n", hnr_mean)) ``` ## Part 5: Batch Processing Process all vowel segments systematically: ```{r batch_processing} # 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) ``` ## Part 6: Data Export and Visualization ### Export to CSV ```{r export_csv} # Save for statistical analysis (tempdir() here so the vignette writes nothing # into your working directory; use a real path in your own scripts) write.csv( vowel_features, file.path(tempdir(), "vowel_acoustic_features.csv"), row.names = FALSE ) ``` ### Visualization `pladdrr` 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")`. ## Real-World Applications ### 1. Sociolinguistic Research Analyze vowel variation across speakers, dialects, or social groups: ```r # 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) ``` ### 2. L2 Acquisition Studies Track vowel development in second language learners: ```r # Longitudinal data (same speaker, multiple sessions) sessions <- c("session1.wav", "session2.wav", "session3.wav") # Measure vowel space area over time # ... (see Example 9 for vowel space calculations) ... ``` ### 3. Clinical Voice Assessment Multi-dimensional voice profiling: ```r # 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. ## Best Practices ### 1. Pre-emphasis for Formants Always pre-emphasize before formant analysis: ```r sound_preemph <- sound$pre_emphasize(from_frequency = 50) formant <- sound_preemph$to_formant_burg(...) ``` ### 2. Appropriate Analysis Parameters Choose parameters based on speaker characteristics: ```r # 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) ``` ### 3. Handle Missing Values Acoustic measurements may fail: ```r f0 <- pitch$get_value_at_time(time, "hertz", interpolate = TRUE) if (is.na(f0)) { # Handle voiceless segments or tracking failures cat("Warning: No F0 at time", time, "\n") } ``` ### 4. Efficient Batch Processing For large corpora, use vectorization and sampling: ```r # Sample large datasets if (nrow(data) > 10000) { data <- data[sample(nrow(data), 1000), ] # Random sample } ``` ## Summary 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 normalization - `vignette("textgrid-workflows")` - Advanced TextGrid manipulation - `inst/examples/` - Complete workflow scripts ## References - Boersma, P., & Weenink, D. ( 2023). *Praat: doing phonetics by computer*. https://praat.org/ - Thomas, E. R., & Kendall, T. ( 2007). *NORM: The vowel normalization and plotting suite*.