Skip to contents

pladdrr Migration Guide

Version 3.0.0 and Beyond

This guide helps existing pladdrr users migrate to the latest API improvements, particularly for heavy users with production code.


Summary of Changes

v2.2.7 (January 2026)

  • Fixed: Batch operations now work with function-wrapper objects
  • Changed: PowerCepstrogram converted from R6 to function wrapper
  • Added: Unified extract_xptr() and unit_to_code() utilities

v2.3.0 (January 2026)

  • Added: Parallel processing API
  • Added: Complete Direct API coverage
  • Deprecated: Duplicate batch query functions
  • Enhanced: Performance documentation

Breaking Changes (None)

All changes are backward compatible. Existing code continues to work.

However, some functions are now deprecated and will show warnings. Update your code to use the preferred alternatives.


Deprecated Functions

Batch Query Functions

Three functions have been deprecated in favor of more consistent naming:

Deprecated (batch-ops.R) Use Instead (batch-queries.R) Status
pitch_get_values_at_times() get_pitch_at_times() Will be removed in v3.0.0
formant_get_values_at_times() get_formants_at_times() Will be removed in v3.0.0
intensity_get_values_at_times() get_intensity_at_times() Will be removed in v3.0.0

Migration:

# OLD (deprecated, shows warning)
f0_values <- pitch_get_values_at_times(pitch, times)

# NEW (preferred)
f0_values <- get_pitch_at_times(pitch, times)

Why? The get_*_at_times() naming is more consistent with the rest of the API.


PowerCepstrogram API Change

PowerCepstrogram was converted from R6 class to function wrapper for consistency.

What Changed

Old R6 style:

# This NO LONGER works
pcg <- PowerCepstrogram$new(.xptr = ptr)

New function wrapper style:

# Use this instead
pcg <- PowerCepstrogram(.xptr = ptr)

Migration Steps

If you create PowerCepstrogram objects:

# OLD (R6)
pcg <- PowerCepstrogram$new(.xptr = some_ptr)

# NEW (function wrapper)
pcg <- PowerCepstrogram(.xptr = some_ptr)

If you use PowerCepstrogram methods:

No changes needed. All methods work identically:

# These work exactly the same in both versions
cpps <- pcg$get_cpps()
mean_cpp <- pcg$get_mean_cpp()
smooth_pcg <- pcg$smooth(0.001, 0.0005)

Why This Change?

  • Consistency: Matches Sound, Pitch, Formant, etc.
  • Simplicity: One less dependency on R6

Pointer Extraction Changes

If you work with external pointers directly, the extraction pattern has been standardized.

Old Pattern (May Fail)

# This relied on R6 private environment
ptr <- obj$.__enclos_env__$private$ptr  # Fails with new objects

New Pattern (Always Works)

# Use the standardized utility (internal, not exported - call with :::)
ptr <- pladdrr:::extract_xptr(obj, "Sound")

# Or access .xptr field directly
ptr <- obj$.xptr

# Or use method (if available)
ptr <- obj$get_xptr()

For Batch Operations

The batch operations in batch-ops.R have been fixed to handle modern objects correctly. No code changes needed if you just call the batch functions.


Performance Optimization Opportunities

New Parallel Processing API (v2.3.0)

If you process many files, you can now easily use parallel processing:

# OLD: Sequential processing
results <- lapply(files, function(f) {
  sound <- Sound(f)
  pitch <- sound$to_pitch()
  pitch$get_mean(0, 0, "hertz")
})

# NEW: Parallel processing
results <- analyze_files_parallel(files, function(sound) {
  pitch <- sound$to_pitch()
  pitch$get_mean(0, 0, "hertz")
}, n_cores = 4)

Notes: - Wraps your existing per-file analysis code with no other changes - Automatic platform detection (Unix/Windows) - Auto-detects optimal core count

New Direct API Functions (v2.3.0)

Four new direct conversion functions that skip R6 object dispatch:

# OLD: Standard API
spec <- sound$to_spectrum()
spg <- sound$to_spectrogram()
ltas <- sound$to_ltas()
pp <- sound$to_point_process_periodic_cc()

# NEW: Direct API
spec_ptr <- to_spectrum_direct(sound)
spg_ptr <- to_spectrogram_direct(sound)
ltas_ptr <- to_ltas_direct(sound)
pp_ptr <- to_point_process_direct(sound)

# Wrap if needed
spec <- Spectrum(.xptr = spec_ptr)

1. Replace Deprecated Functions

Action Required: Update deprecated function calls to avoid warnings.

# Before
f0_vals <- pitch_get_values_at_times(pitch, times)
f1_vals <- formant_get_values_at_times(formant, times, 1)
int_vals <- intensity_get_values_at_times(intensity, times)

# After
f0_vals <- get_pitch_at_times(pitch, times)
f1_vals <- get_formants_at_times(formant, times, formant_numbers = 1)$F1
int_vals <- get_intensity_at_times(intensity, times)

Note: get_formants_at_times() returns a list with F1, F2, F3, F4:

# Get all formants at once
formants <- get_formants_at_times(formant, times, formant_numbers = 1:4)
f1_vals <- formants$F1
f2_vals <- formants$F2
f3_vals <- formants$F3
f4_vals <- formants$F4

2. Use Parallel Processing for Large Batches

Opportunity: If you process >50 files, consider parallel processing.

# Before: Sequential
corpus_files <- list.files("corpus/", pattern = "\\.wav$", full.names = TRUE)
results <- lapply(corpus_files, function(file) {
  sound <- Sound(file)
  # ... analysis code ...
})

# After: Parallel
results <- analyze_files_parallel(corpus_files, function(sound) {
  # ... same analysis code ...
}, n_cores = 4)

3. Optimize Inner Loops with Direct API

Opportunity: If you have performance-critical loops, use Direct API.

# Before: Standard API in loop
for (i in 1:length(sounds)) {
  pitch <- sounds[[i]]$to_pitch()
  stats <- list(
    mean = pitch$get_mean(0, 0, "hertz"),
    sd = pitch$get_standard_deviation(0, 0, "hertz"),
    min = pitch$get_minimum(0, 0, "hertz"),
    max = pitch$get_maximum(0, 0, "hertz")
  )
  # ... use stats ...
}

# After: Direct API
for (i in 1:length(sounds)) {
  pitch_ptr <- to_pitch_direct(sounds[[i]]$.xptr)
  stats <- get_pitch_stats_direct(pitch_ptr)  # All stats in one call
  # ... use stats ...
}

# Even better: Batch + Direct
pitch_ptrs <- sound_to_pitch_batch(sounds, return_r6 = FALSE)
all_stats <- lapply(pitch_ptrs, get_pitch_stats_direct)

Unit Code Standardization

If you work with Praat unit codes directly, use the new unit_to_code() utility (internal, not exported - call with pladdrr:::):

# Before: Inconsistent mappings in different files
unit_code <- switch(unit, hertz = 0L, mel = 2L, erb = 3L)  # Different files had different codes!

# After: Standardized utility
unit_code <- pladdrr:::unit_to_code("hertz", "pitch")  # Returns 0L
unit_code <- pladdrr:::unit_to_code("mel", "pitch")     # Returns 2L
unit_code <- pladdrr:::unit_to_code("bark", "formant")  # Returns 1L

Testing Your Migration

Step 1: Check for Deprecation Warnings

Run your code and look for warnings:

# If you see this warning:
# Warning: pitch_get_values_at_times() is deprecated. Use get_pitch_at_times() instead.

# Update your code accordingly

Step 2: Verify Results

After updating, verify results match:

# Old function (deprecated)
old_result <- pitch_get_values_at_times(pitch, times)

# New function
new_result <- get_pitch_at_times(pitch, times)

# Should be identical
all.equal(old_result, new_result)

Common Migration Scenarios

Scenario 1: AVQI Voice Quality Analysis

# Before (v2.2.6)
sound <- Sound(system.file("extdata", "test.wav", package = "pladdrr"))
tg <- TextGrid(system.file("extdata", "test.TextGrid", package = "pladdrr"))

# Extract intervals
n_intervals <- tg$get_number_of_intervals(1)
intervals <- lapply(1:n_intervals, function(i) {
  list(
    start = tg$get_interval_start_time(1, i),
    end = tg$get_interval_end_time(1, i)
  )
})

# Extract and analyze, one interval at a time
results <- lapply(intervals, function(int) {
  part <- sound$extract_part(int$start, int$end)
  formant <- part$to_formant()
  # ...
})

# After (v2.3.0)
sound <- Sound(system.file("extdata", "test.wav", package = "pladdrr"))
tg <- TextGrid(system.file("extdata", "test.TextGrid", package = "pladdrr"))

# Batch extraction
intervals <- tg$get_all_intervals(1)
start_times <- intervals$start
end_times <- intervals$end

# Combined extract + analyze in one call
formants <- sound_extract_and_formant(sound, start_times, end_times)

Scenario 2: Large Corpus Analysis

# Before (v2.2.6)
files <- list.files("corpus/", pattern = "\\.wav$", full.names = TRUE)

results <- lapply(files, function(f) {
  sound <- Sound(f)
  pitch <- sound$to_pitch()
  
  list(
    file = basename(f),
    mean_f0 = pitch$get_mean(0, 0, "hertz"),
    sd_f0 = pitch$get_standard_deviation(0, 0, "hertz")
  )
})

# After (v2.3.0) - Parallel + Direct API
# analysis_func receives only a Sound object, not its source path, so pair
# the results back up with `files` afterwards (or with names(results)).
results <- analyze_files_parallel(files, function(sound) {
  pitch_ptr <- to_pitch_direct(sound$.xptr)
  stats <- get_pitch_stats_direct(pitch_ptr)

  list(
    mean_f0 = stats$mean,
    sd_f0 = stats$stdev
  )
}, n_cores = 4)
names(results) <- basename(files)

Scenario 3: Formant Tracking

# Before (v2.2.6)
sound <- Sound(system.file("extdata", "test.wav", package = "pladdrr"))
formant <- sound$to_formant()
times <- seq(0.1, 0.5, by = 0.001)

# One R->C crossing per time point
f1_values <- vapply(times, function(t) {
  formant$get_value_at_time(1, t, "hertz")
}, numeric(1))

# After (v2.3.0) - Vectorized, one R->C crossing for the whole vector
f1_values <- get_formants_at_times(formant, times, formant_numbers = 1)$F1

Getting Help

If you encounter issues during migration:

  1. Check the documentation:

  2. Look for deprecation warnings - They tell you exactly what to change

  3. Test with small datasets first - Verify changes work before running on full corpus

  4. Measure your own workload - Use system.time()/microbenchmark to see whether a change helps your pipeline


Timeline

  • v2.2.7 (Jan 2026): Bug fixes, no breaking changes
  • v2.3.0 (Jan 2026): Deprecation warnings added
  • v2.4.0 (TBD): Deprecated functions still work but show louder warnings
  • v3.0.0 (TBD): Deprecated functions removed

Recommendation: Update your code now to avoid warnings and prepare for v3.0.0.


Quick Reference

Function Replacements

Old (Deprecated) New (Preferred) Change Required
pitch_get_values_at_times() get_pitch_at_times() Rename only
formant_get_values_at_times() get_formants_at_times() Rename + access $F1, $F2
intensity_get_values_at_times() get_intensity_at_times() Rename only
PowerCepstrogram$new() PowerCepstrogram() Remove $new

New Features to Adopt

Feature Function What It Does
Parallel processing analyze_files_parallel() Distributes file-level work across cores
Direct API to_*_direct() Skips R6 object dispatch
Batch stats get_pitch_stats_direct() Returns all statistics in one call
Combined ops sound_extract_and_pitch() Combines extraction and analysis in one call

Summary

  1. No breaking changes - existing code works
  2. Update deprecated functions - simple renames
  3. Adopt new APIs - parallel processing and Direct API skip R6 dispatch overhead
  4. Read new docs - see vignette("performance-optimization")