When working with sound files obtained from various sources it is common to have variation in recording formats and parameters or even find corrupt files. Similarly, when a large number of annotations are used, it is normal to find errors in some of them. These problems may prevent the use of acoustic analysis in warbleR. Luckily, the package also offers functions to facilitate the detection and correction of errors in sound files and annotations.
The mp32wav()
function allows you to convert files in ‘.mp3’ format to ‘.wav’ format. This function converts all the ‘mp3’ files in the working directory. Let’s use the files in the ‘./examples/mp3’ folder as an example:
warbleR_options(wav.path = "./examples", ovlp = 90)
list.files(path = "./examples/mp3")
mp32wav(path = "./examples/mp3")
list.files(path = "./examples/mp3")
## [1] "BlackCappedVireo.mp3" "BowheadWhaleSong.mp3"
## [3] "CanyonWren.mp3" "converted_sound_files"
## [5] "Map of Colibri coruscans recordings.jpeg" "Map of Colibri cyanotus recordings.jpeg"
## [7] "Map of Colibri delphinae recordings.jpeg"
## [1] "BlackCappedVireo.mp3" "BlackCappedVireo.wav"
## [3] "BowheadWhaleSong.mp3" "BowheadWhaleSong.wav"
## [5] "CanyonWren.mp3" "CanyonWren.wav"
## [7] "converted_sound_files" "Map of Colibri coruscans recordings.jpeg"
## [9] "Map of Colibri cyanotus recordings.jpeg" "Map of Colibri delphinae recordings.jpeg"
We can also modify the sampling rate and/or dynamic range with mp32wav()
:
mp32wav(path = "./examples/mp3", samp.rate = 48, bit.depth = 24, overwrite = TRUE)
list.files(path = "./examples/mp3")
We can check the properties of the ‘.wav’ sound files using the info_sound_files()
function:
info_sound_files(path = "./examples/mp3")
sound.files | duration | sample.rate | channels | bits | wav.size | samples |
---|---|---|---|---|---|---|
BlackCappedVireo.mp3 | 5.45959 | 22.05 | 1 | 16 | 0.470703 | 120384 |
BlackCappedVireo.wav | 5.45959 | 22.05 | 1 | 16 | 0.470703 | 120384 |
BowheadWhaleSong.mp3 | 86.64816 | 22.05 | 1 | 16 | 7.462891 | 1910592 |
BowheadWhaleSong.wav | 86.64816 | 22.05 | 1 | 16 | 7.462891 | 1910592 |
CanyonWren.mp3 | 5.43347 | 44.10 | 1 | 16 | 0.935547 | 239616 |
CanyonWren.wav | 5.43347 | 44.10 | 1 | 16 | 0.935547 | 239616 |
Alternatively, we can use the fix_wavs()
function to homogenize the sampling rate, the dynamic interval and the number of channels. It is adviced that all sound files should have the same recording parameters before any acoustic analysis. In the example ‘.mp3’ files, not all of them have been recorded with the same parameters. We can see this if we convert them back to ‘.wav’ and see their properties:
mp32wav(path = "./examples/mp3", overwrite = TRUE)
info_sound_files(path = "./examples/mp3")
sound.files | duration | sample.rate | channels | bits | wav.size | samples |
---|---|---|---|---|---|---|
BlackCappedVireo.mp3 | 5.45959 | 22.05 | 1 | 16 | 0.470703 | 120384 |
BlackCappedVireo.wav | 5.45959 | 22.05 | 1 | 16 | 0.470703 | 120384 |
BowheadWhaleSong.mp3 | 86.64816 | 22.05 | 1 | 16 | 7.462891 | 1910592 |
BowheadWhaleSong.wav | 86.64816 | 22.05 | 1 | 16 | 7.462891 | 1910592 |
CanyonWren.mp3 | 5.43347 | 44.10 | 1 | 16 | 0.935547 | 239616 |
CanyonWren.wav | 5.43347 | 44.10 | 1 | 16 | 0.935547 | 239616 |
The fix_wavs()
function will convert all files to the same sampling rate and dynamic range:
fix_wavs(path = mp3.pth, samp.rate = 44.1, bit.depth = 24)
info_sound_files(path = "./examples/mp3/converted_sound_files")
sound.files | duration | sample.rate | channels | bits | wav.size | samples |
---|---|---|---|---|---|---|
BlackCappedVireo.wav | 5.45959 | 44.1 | 1 | 24 | 1.41113 | 240768 |
BowheadWhaleSong.wav | 86.64816 | 44.1 | 1 | 24 | 22.38965 | 3821184 |
CanyonWren.wav | 5.43347 | 44.1 | 1 | 24 | 1.40430 | 239616 |
Another useful function to check file properties is wav_dur()
. This function returns the duration in seconds of each ‘.wav’ file.
check_sound_files()
should be the first function that should be used before running any warbleR analysis. The function simply checks if the sound files in ‘.wav’ format in the working directory can be read in R. For example, the following code checks all the files in the ‘examples’ folder, which should detect the ‘corrupted_file.wav’:
check_sound_files()
## Some file(s) cannot be read
## [1] "corrupted_file.wav"
If we remove that file from the folder, the function returns the following message:
check_sound_files()
## All files can be read
The parameters that determine the appearance of spectrograms (and power spectra and periodgrams) also have an effect on the measurements taken on them. Therefore it is necessary to use the same parameters to analyze all the signals in a project (except with some exceptions) so that the measurements are comparable. The visualization of spectrograms generated with different spectrographic parameters is a useful way of defining the combination of parameters with which the structure of the signals is distinguished in more detail. The function tweak_spectro()
aims to simplify the selection of parameters through the display of spectrograms. The function plots, for a single selection, a mosaic of spectrograms with different display parameters. For numerical arguments, the upper and lower limits of a range can be provided. The following parameters may have variable values:
The following code generates an image with spectrograms that vary in window size and window function (the rest of the parameters are passed to the catalog ()
function internally to create the mosaic):
tweak_spectro(X = lbh_selec_table, wl = c(100, 1000), wn = c("hanning", "hamming", "rectangle"),
length.out = 16, nrow = 8, ncol = 6, width = 15, height = 20,
rm.axes = TRUE, cex = 1, box = F)
Note that the length.out
argument defines the number of values to interpolate within the numerical ranges. wl = 220
seems to produce clearer spectrograms.
We can add a color palette to differentiate the levels of one of the parameters, for example ‘wn’:
#install.packages("RColorBrewer")
library(RColorBrewer)
# crear paleta
cmc <- function(n) if(n > 5) rep(adjustcolor(brewer.pal(5, "Spectral"), alpha.f = 0.6), ceiling(n/4))[1:n] else adjustcolor(brewer.pal(n, "Spectral"), alpha.f = 0.6)
tweak_spectro(X = lbh_selec_table, wl = c(100, 1000), wn = c("hanning", "hamming", "rectangle"),
length.out = 16, nrow = 8, ncol = 6, width = 15, height = 20,
rm.axes = TRUE, cex = 1, box = F, group.tag = "wn",
tag.pal = list(cmc))
We can also use it to choose the color palette and the minimum amplitude for plotting (‘collev.min’):
tweak_spectro(X = lbh_selec_table, wl = 220, collev.min = c(-20, -100), pal = c("reverse.gray.colors.2", "reverse.topo.colors", "reverse.terrain.colors"), length.out = 16, nrow = 8, ncol = 6, width = 15, height = 20, rm.axes = TRUE, cex = 1, box = F, group.tag = "pal", tag.pal = list(cmc))
The main function to double-check selection tables is check_sels()
. This function checks a large number of possible errors in the selection information:
cs <- check_sels(lbh_selec_table)
## all selections are OK
The function returns a data frame that includes the information in ‘X’ plus additional columns about the format of the sound files, as well as the result of the checks (column ‘check.res’):
cs
sound.files | channel | selec | start | end | bottom.freq | top.freq | song | check.res | duration | min.n.samples | sample.rate | channels | bits | sound.file.samples |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Phae.long1.wav | 1 | 1 | 1.169355 | 1.342388 | 2.22011 | 8.60438 | 1 | OK | 0.173033 | 3893 | 22.5 | 1 | 16 | 56251 |
Phae.long1.wav | 1 | 2 | 2.158408 | 2.321457 | 2.16944 | 8.80705 | 1 | OK | 0.163048 | 3668 | 22.5 | 1 | 16 | 56251 |
Phae.long1.wav | 1 | 3 | 0.343337 | 0.518255 | 2.21829 | 8.75660 | 1 | OK | 0.174919 | 3935 | 22.5 | 1 | 16 | 56251 |
Phae.long2.wav | 1 | 1 | 0.159598 | 0.292169 | 2.31686 | 8.82232 | 2 | OK | 0.132571 | 2982 | 22.5 | 1 | 16 | 38251 |
Phae.long2.wav | 1 | 2 | 1.457058 | 1.583209 | 2.28401 | 8.88803 | 2 | OK | 0.126150 | 2838 | 22.5 | 1 | 16 | 38251 |
Phae.long3.wav | 1 | 1 | 0.626552 | 0.757771 | 3.00683 | 8.82232 | 2 | OK | 0.131220 | 2952 | 22.5 | 1 | 16 | 49500 |
Phae.long3.wav | 1 | 2 | 1.974213 | 2.104392 | 2.77684 | 8.88803 | 3 | OK | 0.130179 | 2929 | 22.5 | 1 | 16 | 49500 |
Phae.long3.wav | 1 | 3 | 0.123364 | 0.254581 | 2.31686 | 9.31515 | 3 | OK | 0.131217 | 2952 | 22.5 | 1 | 16 | 49500 |
Phae.long4.wav | 1 | 1 | 1.516812 | 1.662236 | 2.51400 | 9.21659 | 3 | OK | 0.145425 | 3272 | 22.5 | 1 | 16 | 72000 |
Phae.long4.wav | 1 | 2 | 2.932692 | 3.076878 | 2.57971 | 10.23512 | 4 | OK | 0.144186 | 3244 | 22.5 | 1 | 16 | 72000 |
Phae.long4.wav | 1 | 3 | 0.145398 | 0.290497 | 2.57971 | 9.74228 | 4 | OK | 0.145099 | 3264 | 22.5 | 1 | 16 | 72000 |
Let’s modified a selection table to see how the function works:
# copiar las primeras 6 filas
st2 <- lbh_selec_table[1:6, ]
# hacer caracter
st2$sound.files <- as.character(st2$sound.files)
# cambiar nombre de archivo de sonido en sel 1
st2$sound.files[1] <- "aaa.wav"
# modificar fin en sel 3
st2$end[3] <- 100
# hacer top.freq igual q bottom freq en sel 3
st2$top.freq[3] <- st2$bottom.freq[3]
# modificar top freq en sel 5
st2$top.freq[5] <- 200
# modificar channes en sel 6
st2$channel[6] <- 3
#revisar
cs <- check_sels(st2)
cs[, c(1:7, 10)]
##
## some selections listed as having more than 1 channel found in sound files with only 1 channel; channel field relabeled as '1'
## 3 selection(s) are not OK
sound.files | channel | selec | start | end | bottom.freq | top.freq | duration |
---|---|---|---|---|---|---|---|
aaa.wav | 1 | 1 | 1.169355 | 1.342388 | 2.22011 | 8.60438 | NA |
Phae.long1.wav | 1 | 2 | 2.158408 | 2.321457 | 2.16944 | 8.80705 | 0.163048 |
Phae.long1.wav | 1 | 3 | 0.343337 | 100.000000 | 2.21829 | 2.21829 | 99.656663 |
Phae.long2.wav | 1 | 1 | 0.159598 | 0.292169 | 2.31686 | 8.82232 | 0.132571 |
Phae.long2.wav | 1 | 2 | 1.457058 | 1.583209 | 2.28401 | 200.00000 | 0.126150 |
Phae.long3.wav | 1 | 1 | 0.626552 | 0.757771 | 3.00683 | 8.82232 | 0.131220 |
check_sels()
is used internally when creating selection tables and extended selection tables.
Once the information in the selections has been verified, the next step is to ensure that the selections contain accurate information about the location of the signals of interest. This can be done by creating spectrograms of all selections. For this we have several options. The first is spectrograms()
(previously called specreator()
) which generates (by default) a spectrogram for each selection. We can run it on the sample data like this:
# using default parameters tweak_spectro()
warbleR_options(wav.path = "./examples", wl = 220, wn = "hanning", ovlp = 90, pal = reverse.topo.colors)
spectrograms(lbh_selec_table, collevels = seq(-100, 0, 5))
The images it produces are saved in the working directory and look like this:
Exercise
sel.labels
argument
We can create spectrograms for the whole sound files using full_spectrograms()
. If the X
argument is not given, the function will create the spectrograms for all the files in the working directory. Otherwise, the function generates spectrograms for sound files in X
and highlights selections with transparent rectangles similar to those ofspectrograms()
. In this example we download a recording from a striped-throated hermit (Phaethornis striigularis) from Xeno-Canto:
# load package with color palettes
library(viridis)
# create directory
dir.create("./examples/hermit")
# download sound file
phae.stri <- query_xc(qword = "nr:154074", download = TRUE, path = "./examples/hermit")
# Convert mp3 to wav format
mp32wav(path = "./examples/hermit/", pb = FALSE)
# plot full spec
full_spectrograms(sxrow = 1, rows = 10, pal = magma, wl = 200, flim = c(3, 10),
collevels = seq(-140, 0, 5), path = "./examples/hermit/")
Catalogs allow you to inspect selections of many recordings in the same image and group them by categories. This makes it easier to verify the consistency of the categories. Many of the arguments are shared with tweak_spectro()
(catalog()
is used internally in tweak_spectro()
). We can generate a catalog with color tags to identify selections from the same sound file as follows:
# read bat inquiry data
inq <- readRDS(file = "ext_sel_tab_inquiry.RDS")
catalog(X = inq[1:100, ], flim = c(10, 50), nrow = 10, ncol = 10,
same.time.scale = T, mar = 0.01, gr = FALSE, img.suffix = "inquiry",
labels = c("sound.files", "selec"), legend = 0, rm.axes = TRUE,
box = F, group.tag = "sound.files", tag.pal = list(magma),
width = 20, height = 20, pal = viridis, collevels = seq(-100, 0, 5))
Exercise
The position of the selections in the sound file (i.e. its ‘coordinates’ of time and frequency) can be modified interactively from R using the sel_tailor()
function. This function produces a graphic window showing spectrograms and a series of ‘buttons’ that allow you to modify the view and move forward in the selection table:
tailor_sels(X = lbh_selec_table[1:4, ], auto.next = TRUE)
The function returns the corrected data as a data frame in R and also saves a ‘.csv’ file in the directory where the sound files are located.
sel_tailor()
can also be used to modify frequency contours such as those produced by the dfDTW()
or ffDTW()
function:
cntours <- freq_ts(X = lbh_selec_table[1:5, ])
tail.cntours <-tailor_sels(X = lbh_selec_table[1:5, ], ts.df = cntours,
auto.contour = TRUE)
Session information
## R version 4.1.1 (2021-08-10)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.2 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
##
## locale:
## [1] LC_CTYPE=es_ES.UTF-8 LC_NUMERIC=C LC_TIME=es_CR.UTF-8
## [4] LC_COLLATE=es_ES.UTF-8 LC_MONETARY=es_CR.UTF-8 LC_MESSAGES=es_ES.UTF-8
## [7] LC_PAPER=es_CR.UTF-8 LC_NAME=C LC_ADDRESS=C
## [10] LC_TELEPHONE=C LC_MEASUREMENT=es_CR.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.3.4 warbleR_1.1.27 NatureSounds_1.0.4 knitr_1.37 seewave_2.2.0
## [6] tuneR_1.3.3.1
##
## loaded via a namespace (and not attached):
## [1] nlme_3.1-152 bitops_1.0-7 spatstat.sparse_2.1-0 webshot_0.5.2
## [5] httr_1.4.2 Deriv_4.1.3 tools_4.1.1 bslib_0.2.5.1
## [9] DT_0.19 ohun_0.1.0 vegan_2.5-7 utf8_1.2.2
## [13] R6_2.5.1 rpart_4.1-15 mgcv_1.8-36 DBI_1.1.1
## [17] colorspace_2.0-3 permute_0.9-5 raster_3.5-11 withr_2.5.0
## [21] sp_1.4-6 gridExtra_2.3 tidyselect_1.1.1 moments_0.14
## [25] bioacoustics_0.2.8 compiler_4.1.1 soundgen_2.2.0 cli_3.2.0
## [29] rvest_1.0.1 formatR_1.11 xml2_1.3.2 microbenchmark_1.4-7
## [33] sass_0.4.0 xaringanthemer_0.4.1 scales_1.1.1 spatstat.data_2.1-2
## [37] pbapply_1.5-0 proxy_0.4-26 dtw_1.22-3 goftest_1.2-3
## [41] systemfonts_1.0.2 stringr_1.4.0 digest_0.6.29 Sim.DiffProc_4.8
## [45] shinyBS_0.61 spatstat.utils_2.3-0 rmarkdown_2.10 svglite_2.0.0
## [49] pkgconfig_2.0.3 htmltools_0.5.2 PhenotypeSpace_0.1.0 fastmap_1.1.0
## [53] highr_0.9 htmlwidgets_1.5.3 rlang_1.0.2 rstudioapi_0.13
## [57] xaringan_0.22 baRulho_1.0.6 jquerylib_0.1.4 farver_2.1.0
## [61] generics_0.1.0 jsonlite_1.7.2 dplyr_1.0.7 RCurl_1.98-1.6
## [65] magrittr_2.0.2 Matrix_1.3-4 Rcpp_1.0.8 munsell_0.5.0
## [69] fansi_1.0.2 viridis_0.6.2 abind_1.4-5 lifecycle_1.0.1
## [73] terra_1.5-12 whisker_0.4 stringi_1.7.6 yaml_2.3.5
## [77] MASS_7.3-54 grid_4.1.1 parallel_4.1.1 crayon_1.5.0
## [81] deldir_1.0-6 lattice_0.20-44 splines_4.1.1 tensor_1.5
## [85] pillar_1.7.0 rjson_0.2.21 spatstat.geom_2.3-1 fftw_1.0-6.1
## [89] codetools_0.2-18 glue_1.6.2 evaluate_0.15 remotes_2.4.0
## [93] png_0.1-7 vctrs_0.3.8 polyclip_1.10-0 gtable_0.3.0
## [97] purrr_0.3.4 spatstat.core_2.3-2 assertthat_0.2.1 ggplot2_3.3.5
## [ reached getOption("max.print") -- omitted 7 entries ]