| name | r-bioacoustics |
| description | Expert bioacoustic analysis in R using tuneR, seewave, warbleR, bioacoustics, ohun, and soundecology. Use when mentions "análise de áudio em R", "audio analysis in R", "bioacoustics", "bioacústica", "acoustic analysis", "análise acústica", "espectrograma", "spectrogram", "MFCC", "mel-spectrogram", "detecção de eventos sonoros", "event detection", "sound detection", "passive acoustic monitoring", "PAM", "monitoramento acústico passivo", "índices ecoacústicos", "ecoacoustic indices", "acoustic indices", "tuneR", "seewave", "warbleR", "bioacoustics package", "ohun", "soundecology", "feature extraction from audio", "extração de features de áudio", "classificação de sons", "sound classification", "bird sound", "som de aves", "animal sound", "análise de vocalizações", "vocalization analysis", "acoustic ecology", "ecologia acústica", or working with environmental audio, animal vocalizations, or soundscape analysis. |
| version | 1.0.0 |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
R Bioacoustics Expert
Expert guidance for bioacoustic analysis in R using the comprehensive ecosystem of tuneR, seewave, warbleR, bioacoustics, ohun, and soundecology packages.
Overview
This skill provides expert knowledge for analyzing environmental audio, animal vocalizations, and soundscapes in R. It covers:
- Audio I/O and preprocessing (tuneR)
- Spectral and temporal analysis (seewave)
- Bioacoustic signal processing (warbleR)
- Automated detection and feature extraction (bioacoustics)
- Event detection optimization (ohun)
- Ecoacoustic indices (soundecology)
The skill integrates these packages into coherent workflows for Passive Acoustic Monitoring (PAM), species classification, soundscape ecology, and bioacoustic research.
Core Packages
tuneR - Audio Foundation
Purpose: Audio I/O, basic manipulation, MFCC extraction
Key Functions: readWave(), writeWave(), melfcc(), normalize(), downsample(), mono()
Role: Foundation for all bioacoustic workflows - handles audio files and basic transformations
seewave - Comprehensive Analysis
Purpose: Advanced spectral/temporal analysis, acoustic indices (200+ functions)
Key Functions: spectro(), meanspec(), specprop(), ACI(), H(), ffilter(), zcr()
Role: Core analysis engine - spectrograms, features, filtering, visualization
warbleR - Bioacoustic Pipeline
Purpose: Streamlined workflow for animal vocalization analysis
Key Functions: auto_detec(), specan() (22 parameters), cross_correlation(), dfDTW()
Role: High-level bioacoustic workflows - detection, measurement, comparison
bioacoustics - Automated Detection
Purpose: Robust automated detection and feature extraction
Key Functions: blob_detection(), threshold_detection(), acoustic_complexity()
Role: Production-grade detection with Kalman filtering for noisy recordings
ohun - Detection Optimization
Purpose: Optimize detection parameters for your specific recordings
Key Functions: optimize_energy_detector(), optimize_template_detector()
Role: Tune detection thresholds to maximize precision/recall trade-offs
soundecology - Soundscape Indices
Purpose: Ecoacoustic indices for biodiversity and soundscape assessment
Key Functions: acoustic_complexity(), acoustic_diversity(), acoustic_evenness()
Role: Soundscape-level metrics (ACI, ADI, AEI, BI, NDSI)
Core Workflows
Workflow 1: Basic Audio Exploration
Goal: Load, inspect, and clean audio files
library(tuneR)
library(seewave)
audio <- readWave("recording.wav")
print(audio)
duration(audio)
audio_norm <- normalize(audio, unit = "16")
if (audio@stereo) {
audio_mono <- mono(audio, which = "both")
}
audio_resamp <- downsample(audio_mono, samp.rate = 22050)
oscillo(audio_resamp, from = 0, to = 5)
spectro(audio_resamp, flim = c(0, 11))
Best Practices:
- Always check sample rate, duration, channels before analysis
- Normalize to consistent amplitude scale
- Mono conversion:
which = "left", "right", or "both" (average)
- Resample based on target frequencies (e.g., 22050 Hz for birds, 44100 Hz for full range)
Workflow 2: Spectrogram Analysis
Goal: Generate and analyze spectrograms with appropriate parameters
library(seewave)
spectro(audio,
wl = 512,
ovlp = 90,
flim = c(2, 10),
collevels = seq(-40, 0, 1))
spec_t <- spec(audio, at = 2.5, plot = TRUE)
mean_spec <- meanspec(audio, flim = c(0, 10), plot = TRUE)
props <- specprop(mean_spec)
dom_freq <- dfreq(audio,
wl = 512,
ovlp = 90,
plot = TRUE,
threshold = 5)
peaks <- fpeaks(mean_spec,
nmax = 5,
plot = TRUE)
Parameter Selection:
- Window length (
wl):
- Smaller (256-512): Better temporal resolution, worse frequency resolution
- Larger (1024-2048): Better frequency resolution, worse temporal resolution
- Rule:
wl should be > 2 / min_frequency_of_interest
- Overlap (
ovlp): 70-90% for smooth spectrograms
- Frequency limits (
flim): Match your species' vocalization range
Workflow 3: Automated Signal Detection
Goal: Detect vocalization events in continuous recordings
library(warbleR)
library(bioacoustics)
library(ohun)
detections_wr <- auto_detec(
ssmooth = 300,
threshold = 10,
mindur = 0.05,
maxdur = 2,
bp = c(2, 10),
path = "audio_folder"
)
detections_ba <- blob_detection(
audio,
time_exp = 5,
min_dur = 20,
max_dur = 2000,
min_area = 40,
min_TBE = 20,
max_TBE = 5000,
LPF = 10000,
HPF = 2000
)
reference <- data.frame(
sound.files = "recording.wav",
start = c(1.2, 3.5, 5.8),
end = c(1.5, 3.9, 6.1)
)
opt_params <- optimize_energy_detector(
reference = reference,
path = "audio_folder",
bp = c(2, 10),
hop.size = 11.6,
wl = 512
)
detections_oh <- energy_detector(
files = "recording.wav",
bp = c(2, 10),
threshold = opt_params$threshold,
smooth = opt_params$smooth,
path = "audio_folder"
)
template <- selection_table(
sound.files = "template.wav",
start = 0.1,
end = 0.3
)
template_detections <- template_detector(
templates = template,
files = "recording.wav",
path = "audio_folder",
cor.method = "pearson"
)
When to Use Each Method:
- warbleR
auto_detec(): Fast, good for high SNR, simple vocalizations
- bioacoustics
blob_detection(): Best for noisy recordings, complex soundscapes
- ohun energy: When you can optimize with reference annotations
- ohun template: For stereotyped calls with consistent structure
Workflow 4: Feature Extraction for Classification
Goal: Extract comprehensive acoustic features for machine learning
library(tuneR)
library(seewave)
library(warbleR)
library(bioacoustics)
features_specan <- specan(
X = detections_wr,
bp = c(2, 10),
wl = 512,
path = "audio_folder"
)
extract_features <- function(audio, start, end) {
seg <- cutw(audio, from = start, to = end, output = "Wave")
zcr_val <- zcr(seg)
rms_val <- rms(seg)
spec <- meanspec(seg, plot = FALSE)
props <- specprop(spec)
mfcc <- melfcc(seg,
numcep = 13,
wintime = 0.025,
hoptime = 0.010)
mfcc_summary <- colMeans(mfcc)
aci_val <- ACI(seg)
h_val <- H(seg)
c(
duration = end - start,
zcr = zcr_val,
rms = rms_val,
spectral_centroid = props$cent,
spectral_flatness = props$sfm,
spectral_entropy = props$sh,
aci = aci_val,
temporal_entropy = h_val,
setNames(mfcc_summary, paste0("mfcc_", 1:13))
)
}
features_custom <- detections_wr |>
rowwise() |>
mutate(
audio = list(readWave(file.path("audio_folder", sound.files))),
features = list(extract_features(audio[[1]], start, end))
) |>
unnest_wider(features)
Feature Engineering Strategy:
- Time-frequency basics: MFCC, spectral centroid/bandwidth/rolloff, ZCR
- Structural: duration, frequency range, modulation, temporal envelope
- Acoustic indices: ACI, entropy (complement, not primary for classification)
- Statistical summaries: mean, sd, quartiles for time-varying features
See references/feature-engineering.md for comprehensive feature catalog.
Workflow 5: Ecoacoustic Indices
Goal: Calculate soundscape-level metrics for biodiversity assessment
library(soundecology)
library(seewave)
audio <- readWave("soundscape_5min.wav")
aci_result <- acoustic_complexity(audio,
min_freq = 2000,
max_freq = 10000,
j = 5)
print(aci_result$AciTotAll_left)
adi_result <- acoustic_diversity(audio,
max_freq = 10000,
db_threshold = -50,
freq_step = 1000)
print(adi_result$adi_left)
aei_result <- acoustic_evenness(audio,
max_freq = 10000,
db_threshold = -50,
freq_step = 1000)
print(aei_result$aei_left)
bi_result <- bioacoustic_index(audio,
min_freq = 2000,
max_freq = 8000)
print(bi_result$left_area)
ndsi_result <- ndsi(audio,
fft_w = 1024,
anthro_min = 1000,
anthro_max = 2000,
bio_min = 2000,
bio_max = 11000)
print(ndsi_result$ndsi_left)
all_indices <- multiple_sounds(
directory = "soundscape_folder",
resultfile = "indices_results.csv",
soundindex = c("acoustic_complexity",
"acoustic_diversity",
"acoustic_evenness",
"bioacoustic_index",
"ndsi")
)
When to Use Ecoacoustic Indices:
- ✅ Soundscape characterization and biodiversity monitoring
- ✅ Comparing acoustic activity across sites or time periods
- ✅ As supplementary features for soundscape classification
- ❌ Not recommended as primary features for species classification (limited discrimination)
See references/ecoacoustic-indices.md for detailed interpretation guide.
Workflow 6: Complete PAM Pipeline
Goal: End-to-end Passive Acoustic Monitoring workflow
library(tidyverse)
library(tuneR)
library(seewave)
library(warbleR)
library(bioacoustics)
library(ohun)
project_dir <- "pam_project"
dir.create(file.path(project_dir, c("raw", "processed", "features", "models", "results")), recursive = TRUE)
standardize_audio <- function(file_path, output_dir) {
audio <- readWave(file_path)
if (audio@stereo) audio <- mono(audio, which = "both")
if (audio@samp.rate != 22050) {
audio <- downsample(audio, samp.rate = 22050)
}
audio <- normalize(audio, unit = "16")
output_path <- file.path(output_dir, basename(file_path))
writeWave(audio, output_path)
return(output_path)
}
raw_files <- list.files("pam_project/raw", pattern = "\\.wav$", full.names = TRUE)
processed_files <- map_chr(raw_files, ~standardize_audio(.x, "pam_project/processed"))
detections <- blob_detection(
readWave(processed_files[1]),
time_exp = 1,
min_dur = 50,
max_dur = 2000,
min_area = 40,
LPF = 11000,
HPF = 2000
)
selection_table <- detections |>
mutate(
sound.files = basename(processed_files[1]),
start = starting_time,
end = starting_time + duration,
selec = row_number()
) |>
select(sound.files, selec, start, end, duration, freq_min, freq_max)
features <- specan(
X = selection_table,
bp = c(2, 10),
wl = 512,
path = "pam_project/processed"
)
add_mfcc <- function(row, audio_dir) {
audio <- readWave(file.path(audio_dir, row$sound.files))
segment <- cutw(audio, from = row$start, to = row$end, output = "Wave")
mfcc <- melfcc(segment, numcep = 13)
mfcc_mean <- colMeans(mfcc)
as_tibble(t(mfcc_mean)) |>
set_names(paste0("mfcc_", 1:13))
}
mfcc_features <- selection_table |>
rowwise() |>
mutate(mfcc = list(add_mfcc(cur_data(), "pam_project/processed"))) |>
unnest(mfcc)
features_complete <- bind_cols(features, mfcc_features |> select(starts_with("mfcc_")))
write_csv(features_complete, "pam_project/features/features.csv")
inference_windowed <- function(audio_path, window_sec = 5, overlap = 0.5) {
audio <- readWave(audio_path)
sr <- audio@samp.rate
duration <- duration(audio)
hop <- window_sec * (1 - overlap)
windows <- seq(0, duration - window_sec, by = hop)
map_dfr(windows, function(start) {
segment <- cutw(audio, from = start, to = start + window_sec, output = "Wave")
tibble(
start_time = start,
end_time = start + window_sec,
)
})
}
PAM Best Practices:
- Standardization first: Always resample, normalize, convert to mono
- Temporal/spatial splits: Group by recording_id or site for cross-validation (prevent leakage)
- Detection before classification: Reduce data volume by detecting events first
- Class imbalance: Use class weights, focal loss, or per-species thresholds
- Post-processing: Smooth predictions over time, aggregate overlapping windows
- Reproducibility: Use
{renv} for dependencies, set seeds, track metadata
See examples/pam-pipeline.md for complete reproducible example.
Integration Patterns
Pattern 1: tuneR → seewave Pipeline
audio <- readWave("recording.wav") |>
normalize(unit = "16") |>
mono(which = "both")
spectro(audio, flim = c(0, 10))
features <- specprop(meanspec(audio, plot = FALSE))
aci <- ACI(audio)
Pattern 2: warbleR Batch Processing
detections <- auto_detec(path = "audio_folder", bp = c(2, 10))
features <- specan(X = detections, path = "audio_folder")
correlations <- cross_correlation(X = detections, path = "audio_folder")
Pattern 3: bioacoustics Detection → seewave Features
detections <- blob_detection(audio, min_dur = 50, max_dur = 2000)
for (i in 1:nrow(detections)) {
segment <- cutw(audio,
from = detections$starting_time[i],
to = detections$starting_time[i] + detections$duration[i],
output = "Wave")
spec <- meanspec(segment, plot = FALSE)
features[i, ] <- specprop(spec)
}
Pattern 4: ohun Optimization → Production Detection
opt <- optimize_energy_detector(
reference = reference_annotations,
bp = c(2, 10),
path = "train_folder"
)
production_detections <- energy_detector(
files = list.files("new_recordings"),
bp = c(2, 10),
threshold = opt$threshold,
smooth = opt$smooth,
path = "new_recordings"
)
Pattern 5: soundecology for Soundscape + warbleR for Species
soundscape_summary <- tibble(
file = list.files("recordings", pattern = "\\.wav$"),
aci = map_dbl(file, ~acoustic_complexity(readWave(.x))$AciTotAll_left),
adi = map_dbl(file, ~acoustic_diversity(readWave(.x))$adi_left),
aei = map_dbl(file, ~acoustic_evenness(readWave(.x))$aei_left)
)
species_detections <- auto_detec(path = "recordings", bp = c(2, 10))
species_features <- specan(X = species_detections, path = "recordings")
combined <- species_features |>
left_join(soundscape_summary, by = c("sound.files" = "file"))
Best Practices
Audio Preprocessing
-
Always standardize before analysis:
- Mono conversion (average both channels if stereo)
- Fixed sample rate (22050 Hz for birds, 44100 Hz for full range)
- Normalization to consistent scale
- High-pass filter to remove DC offset and low-frequency noise
-
Choose sample rate based on target species:
- Birds: 22050-44100 Hz (covers up to 11-22 kHz)
- Bats: 192000-384000 Hz (ultrasonic)
- Frogs/amphibians: 22050 Hz
- Marine mammals: varies widely
-
Segmentation strategy:
- Fixed windows: 2-5 seconds with 50% overlap
- Event-based: detect events first, extract variable-length segments
- Hybrid: detect events within fixed windows
Detection Strategy
-
Start simple, add complexity:
- Baseline: warbleR
auto_detec() with visual validation
- If too many false positives: bioacoustics
blob_detection()
- If you have reference: ohun optimization
- For stereotyped calls: ohun template matching
-
Validation is critical:
- Manually annotate 50-100 events as reference
- Calculate precision, recall, F1 at different thresholds
- Optimize for your use case (high precision vs high recall)
-
Handling continuous audio:
- Don't analyze entire long recordings at once (memory issues)
- Use fixed windows (5-10 min) or overlapping chunks
- Aggregate predictions with temporal smoothing
Feature Engineering
-
Feature selection priorities:
- Primary: MFCC, spectral centroid, bandwidth, duration, frequency range
- Structural: Modulation, temporal envelope, inter-note intervals (if applicable)
- Supplementary: Acoustic indices (ACI, entropy), zero-crossing rate
-
Avoid redundant features:
- Many seewave functions return correlated features
- Use correlation analysis or PCA to reduce dimensionality
- Prioritize interpretable features over exhaustive extraction
-
Time-series summarization:
- For time-varying features (e.g., MFCC per frame), compute:
- Mean, standard deviation, min, max, median
- Percentiles (Q25, Q75)
- Delta (first difference) and delta-delta statistics
Cross-Validation
-
Prevent temporal/spatial leakage:
- Group by
recording_id, site_id, or date
- Use
group_vfold_cv() in tidymodels or grouped resampling in mlr3
- Never split randomly - adjacent segments are highly correlated
-
Nested resampling for tuning:
- Outer loop: performance estimation (grouped by recording)
- Inner loop: hyperparameter tuning
- See
{mlr3} nested resampling documentation
-
Class imbalance handling:
- Class weights proportional to inverse frequency
- Focal loss for extreme imbalance
- Per-species threshold tuning for multi-label scenarios
Performance Optimization
-
Batch processing with warbleR:
- Use
parallel argument in warbleR functions
- Process multiple files simultaneously
- Example:
specan(X, parallel = 4)
-
Memory management for long recordings:
- Process in chunks, don't load entire file
- Use
readWave() with from and to parameters
- Clear objects with
rm() and gc() in loops
-
Caching intermediate results:
- Save detection tables as CSV
- Save feature tables as RDS or parquet
- Use
{targets} or {drake} for pipeline management
Common Code Patterns
Pattern: Batch Feature Extraction
library(tidyverse)
library(tuneR)
library(seewave)
extract_all_features <- function(audio_dir, detection_table) {
detection_table |>
rowwise() |>
mutate(
audio = list(readWave(file.path(audio_dir, sound.files))),
segment = list(cutw(audio[[1]], from = start, to = end, output = "Wave")),
mfcc = list(colMeans(melfcc(segment[[1]], numcep = 13))),
spec = list(specprop(meanspec(segment[[1]], plot = FALSE))),
aci_val = ACI(segment[[1]]),
audio = list(NULL),
segment = list(NULL)
) |>
unnest_wider(spec) |>
unnest_wider(mfcc, names_sep = "_")
}
features <- extract_all_features("audio_folder", detections)
Pattern: Quality Control Filtering
filter_detections <- function(detections, min_dur = 0.05, max_dur = 2,
min_freq = 1000, max_freq = 12000) {
detections |>
filter(
duration >= min_dur,
duration <= max_dur,
freq_min >= min_freq,
freq_max <= max_freq,
freq_max - freq_min >= 500
)
}
clean_detections <- filter_detections(raw_detections)
Pattern: Spectrogram Visualization Grid
library(ggplot2)
plot_detection_grid <- function(audio_dir, detections, n = 16) {
sampled <- slice_sample(detections, n = n)
plots <- sampled |>
rowwise() |>
mutate(
plot = list({
audio <- readWave(file.path(audio_dir, sound.files))
seg <- cutw(audio, from = start, to = end, output = "Wave")
spec_data <- spectro(seg, plot = FALSE)
ggspectro(seg, flim = c(2, 10)) +
labs(title = paste("Det", selec))
})
)
cowplot::plot_grid(plotlist = plots$plot, ncol = 4)
}
plot_detection_grid("audio_folder", detections, n = 16)
Pattern: Export for Raven/Audacity
export_raven <- function(detections, output_file) {
raven_format <- detections |>
mutate(
Selection = selec,
View = "Spectrogram 1",
Channel = 1,
`Begin Time (s)` = start,
`End Time (s)` = end,
`Low Freq (Hz)` = freq_min,
`High Freq (Hz)` = freq_max
) |>
select(Selection, View, Channel, `Begin Time (s)`, `End Time (s)`,
`Low Freq (Hz)`, `High Freq (Hz)`)
write_tsv(raven_format, output_file)
}
export_raven(detections, "detections_raven.txt")
Pattern: Reproducible Pipeline with {targets}
library(targets)
library(tarchetypes)
list(
tar_target(raw_files, list.files("raw", pattern = "\\.wav$", full.names = TRUE)),
tar_target(processed_files, standardize_audio(raw_files), pattern = map(raw_files)),
tar_target(detections, detect_events(processed_files)),
tar_target(features, extract_features(detections, processed_files)),
tar_target(model, train_model(features)),
tar_target(predictions, predict_species(model, features))
)
Integration with Other Skills
- r-tidymodels: For training classifiers on extracted features
- r-deeplearning: For CNN/CRNN models on spectrograms (see audio section)
- r-feature-engineering: For advanced feature selection and encoding
- r-timeseries: For temporal pattern analysis in acoustic activity
- ggplot2: For custom acoustic visualizations
- r-performance: For optimizing large-scale PAM pipelines
Troubleshooting
Issue: readWave() fails with "unable to open connection"
- Check file path is correct (use
file.exists())
- Verify file format (WAV, not MP4/M4A)
- Try
readMP3() if file is MP3
Issue: Spectrograms look noisy/unclear
- Adjust
wl (window length) - try 512, 1024, 2048
- Increase
ovlp (overlap) to 85-95%
- Apply bandpass filter with
ffilter() before visualization
- Adjust
collevels to change dynamic range
Issue: Too many false positives in detection
- Increase threshold in
auto_detec() or energy_detector()
- Tighten duration constraints (
mindur, maxdur)
- Use narrower bandpass filter (
bp)
- Switch to
blob_detection() for better noise handling
Issue: Memory issues with long recordings
- Don't load entire file - use
from and to in readWave()
- Process in chunks (e.g., 5-minute segments)
- Clear objects regularly with
rm() and gc()
- Consider downsampling if very high sample rate
Issue: MFCC extraction fails
- Ensure audio is mono (use
mono())
- Check sample rate is reasonable (16000-48000 Hz)
- Reduce
numcep if too many coefficients requested
- Verify segment duration is sufficient (>0.025s)
Additional Resources
Academic References
-
Comprehensive reviews:
- Systematic review of ML in ecoacoustics (2023)
- Advancements in preprocessing, detection and classification for PAM (2024)
-
Methodology papers:
- Weakly-supervised bird sound classification (2021)
- Self-supervised learning for few-shot bird sounds (2023)
- Refining ecoacoustic indices in ecosystems (2024)
-
Datasets:
- AnuraSet: Neotropical anuran dataset
- BirdCLEF challenges (annual)
See references/academic-sources.md for full citations and links.