Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes to Matt's part of the package--bump version to 1.0.3 #21

Merged
merged 12 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: dynaSpec
Type: Package
Title: Dynamic Spectrogram Visualizations
Version: 1.0.2
Version: 1.0.3
Date: 2024-02-20
Description: A set of tools to generate dynamic spectrogram visualizations in video format.
License: GPL (>= 2)
Expand All @@ -13,7 +13,7 @@ URL: https://github.com/maRce10/dynaSpec
BugReports: https://github.com/maRce10/dynaSpec/issues
NeedsCompilation: no
Suggests: parallel, imager, fs
RoxygenNote: 7.3.1
RoxygenNote: 7.3.2
Repository: CRANs
Language: en-US
Encoding: UTF-8
Expand Down
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# *dynaSpec 1.0.3*

* Fix logic for crop and xLim in prep_static_ggspectro()
* Other fixes to have more predictable behavior of exported MP4 videos using "Matt's approach" with paged_spectro()
* Added title parameter to add a title to both static and subsequent paged spectrogram (MP4)
* Added resampleRate parameter to prep_static_ggspectro() for control over resolution of spectrogram (and to speed things up)

# *dynaSpec 1.0.1*

* Fix resampling issue when sampling rate of waves != 44.1 kHz
Expand Down
21 changes: 16 additions & 5 deletions R/ggSpec.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
ggSpec <-
function(wav,
soundFile,
resampleRate,
segLens,
savePNG,
specWidth,
specHeight,
destFolder,
title,
ovlp,
wl,
wn,
Expand All @@ -33,8 +35,7 @@ ggSpec <-
nSegs = length(segLens) - 1
}


# bg="#ebe834"
# bg="#ebe834"
#Font color adapted from https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color

if (is.null(fontAndAxisCol)) {
Expand All @@ -56,6 +57,14 @@ ggSpec <-
# text(1,1,"READABLE?",cex=5,col=contrastFont)



if(!is.null(resampleRate)){
wav0 <- wav #backup
message("Original wav (",[email protected],"samples/sec) data points: ",length(wav0@left))
wav <- tuneR::downsample(wav,samp.rate=resampleRate)
message("Original wav (",[email protected],"samples/sec) data points: ",length(wav@left))
}

#Modified from seewave::ggspectro
#--->
# norm =FALSE is important for having similar gain across different recordings
Expand Down Expand Up @@ -108,13 +117,15 @@ ggSpec <-

level=NULL #just to shut up the check

#Plot that thang
# #

# Plot that thang ---------------------------------------------------------

Glist[[i]] <-
ggplot2::ggplot(df_i, ggplot2::aes(x = time, y = freq, z = amplitude)) +
ggplot2::xlim(segLens[i], segLens[i + 1]) + ggplot2::ylim(yLim) +
#Labels
ggplot2::labs(x = "Time (s)", y = "Frequency (kHz)", fill = "Amplitude\n(dB)\n") +
ggplot2::labs(x = "Time (s)", y = "Frequency (kHz)", fill = "Amplitude\n(dB)\n",
title=title) +
{
#Set scale according to viridis or custom color scheme
if (isViridis) {
Expand Down
22 changes: 11 additions & 11 deletions R/paged_spectro.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
#' is defined by the xLim parameter in \code{\link{prep_static_ggspectro}}. You can also output temporary segmented files, if desired.
#'
#' @aliases pagedSpectro pagedSpec
#' @usage paged_spectro(specParams,destFolder,vidName,framerate=30,highlightCol="#4B0C6BFF",
#' highlightAlpha=.6,cursorCol="#4B0C6BFF",delTemps=TRUE)
#' @param specParams an object returned from \code{\link{prep_static_ggspectro}}
#' @param destFolder destination of output video; this setting overwrites setting from specParams object
#' @param vidName expects "FileName", .mp4 not necessary; if not supplied, will be named after the file you used in prep_static_ggspectro()
#' @param highlightCol default "#4B0C6BFF" (a purple color to match the default viridis 'inferno' palette)
#' @param highlightAlpha opacity of the highlight box; default is 0.6
#' @param cursorCol Color of the leading edge of the highlight box; default "#4B0C6BFF"
#' @param delTemps Default= TRUE, deletes temporary files (specs & WAV files used to create concatenated video)
#' @param cursorCol Color of the leading edge of the highlight box; default "white"
#' @param delete_temp_files Default= TRUE, deletes temporary files (specs & WAV files used to create concatenated video)
#' @param framerate by default, set to 30 (currently this is not supported, as animate doesn't honor the setting)
#' @return Nothing is returned, though progress and file save locations are output to user. Video should play after rendering.
#' @seealso \code{\link{prep_static_ggspectro}}
Expand Down Expand Up @@ -58,7 +56,7 @@
#' # see more examples at https://marce10.github.io/dynaSpec/
#' }

paged_spectro <-function(specParams,destFolder,vidName,framerate=30,highlightCol="#4B0C6BFF",highlightAlpha=.6,cursorCol="#4B0C6BFF",delTemps=TRUE)
paged_spectro <-function(specParams,destFolder,vidName,framerate=30,highlightCol="#4B0C6BFF",highlightAlpha=.6,cursorCol="white",delete_temp_files=TRUE)
{
xmin<-ymin <- xmax <- ymax <- NULL
#This ^^ suppresses note about "no visible binding for global variable ‘xmax’"
Expand Down Expand Up @@ -99,7 +97,7 @@ if(!missing(vidName)){
outWAV<-lapply(1:length(specParams$segWavs),function(x) {paste0(tempdir,iName0,"_",x,"_.wav")})
invisible(
lapply(1:length(specParams$segWavs), function(x){fn=outWAV[[x]]
tuneR::writeWave(specParams$segWavs[[x]],file=fn)
tuneR::writeWave(specParams$segWavs[[x]],filename=fn)
cat(paste0("\nSaved temp wav segment: ",fn))}))
}

Expand Down Expand Up @@ -129,12 +127,12 @@ for(i in 1:length(specParams$segWavs))
cursor<-seq(range_i[1],range_i[2],specParams$xLim[2]/framerate)
played<-data.frame(xmin=cursor,xmax=rep(range_i[2],length(cursor)),ymin=rep(specParams$yLim[1],length(cursor)),ymax=rep(specParams$yLim[2], length(cursor)))


#Make ggplot overlay of highlight box on spectrogram
vidSegment<-{
ggplot2::ggplot(played)+ggplot2::xlim(range_i)+ggplot2::ylim(specParams$yLim)+
#Labels
ggplot2::labs(x="Time (s)",y="Frequency (kHz)",fill="Amplitude\n(dB)\n")+
ggplot2::labs(x="Time (s)",y="Frequency (kHz)",fill="Amplitude\n(dB)\n",
title=specParams$title)+
##Animate() seems to shrink font size a bit
mytheme_lg(specParams$bg)+

Expand Down Expand Up @@ -221,11 +219,13 @@ for(i in 1:length(specParams$segWavs))

}

cat("\n\nAll done!\n")
cat(paste0("file saved @",vidName))
message("\n\nAll done!\nfile saved @",vidName)
system(paste0('open "',vidName,'"'))

if(delTemps){unlink(tempdir,recursive=TRUE);print(paste0("FYI temporary file directory deleted @ ",tempdir))}
if(delete_temp_files){
unlink(tempdir,recursive=TRUE)
message("FYI temporary file directory deleted @ ",tempdir)
}
}#end else which passed FFMPEG check
}#end paged_spectro definition

Expand Down
66 changes: 40 additions & 26 deletions R/prep_static_ggspectro.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,23 @@
#'
#' @aliases prepStaticSpec prepStaticGGspec
#'
#' @usage prep_static_ggspectro(soundFile,destFolder,outFilename,savePNG=FALSE,colPal="inferno",
#' crop=NULL,bg=NULL,filter=NULL,xLim=NULL,yLim=c(0,10),plotLegend=FALSE,onlyPlotSpec=TRUE,
#' ampTrans=1,min_dB=-30,wl=512, ovlp=90,wn="blackman",specWidth=9,specHeight=3,
#' colbins=30,ampThresh=0,bgFlood=FALSE,fontAndAxisCol=NULL,optim=NULL,...)
#'
#' @param soundFile should work with URLs, full and relative paths; handles .mp3 and .wav
#' @param destFolder needs to be like "figures/spectrograms/" to be relative to working directory; goes to soundFile folder by default or working directory if soundFile is a URL; can specify "wd" to output to the working directory
#' @param outFilename if left out, will use input name in output filename
#' @param savePNG save static spectrograms as PNGs? They will be exported to destFolder
#' @param destFolder path to directory to save output. Needs to be like "figures/spectrograms/" to be relative to working directory. Default=parent folder of soundFile. Specify "wd" to output to the working directory, gotten from [get_wd()]
#' @param outFilename name for output PNG. default=NULL will use input name in output filename.
#' @param savePNG logical; Save static spectrograms as PNGs? They will be exported to destFolder.
#' @param colPal color palette; one of "viridis","magma","plasma","inferno","cividis" from the \code{\link[viridis]{viridis}} package OR a 2 value vector (e.g. c("white","black")), defining the start and end of a custom color gradient
#' @param crop subset of recording to include; if crop=NULL, use whole file; if number, interpreted as crop first X.X sec; if c(X1,X2), interpreted as specific time interval in sec
#' @param xLim is the time limit in seconds for all spectrograms; i.e. page width in seconds for multi-page dynamic spectrograms (defaults to WAV file length, unless file duration >5s). To override the 5s limit, put xLim=Inf.
#' @param yLim is the frequency limits (y-axis); default is c(0,10) aka 0-10kHz
#' @param plotLegend include a legend showing amplitude colors?
#' @param onlyPlotSpec do you want to just plot the spec and leave out the legend, axes, and axis labels?
#' @param ampTrans amplitude transform for boosting spectrum contrast; defaults to identity (actual dB values); specify a decimal number for the lambda value of scales::modulus_trans(); 2.5 is a good place to start. (This amplifies your loud values the most, while not increasing background noise much at all)
#' @param crop subset of recording to include; default crop=NULL will use whole file, up to 10 sec; if a number, interpreted as crop first X.X sec; if c(X1,X2), interpreted as trimming out a specific time interval in sec; if crop=FALSE, will not crop at all, even for recordings over 10 sec.
#' @param xLim the time limit (x-axis width) in seconds for all spectrograms; i.e. page width in seconds for multi-page dynamic spectrograms (defaults to WAV file length, unless file duration >5s). To override the 5s limit, put xLim=Inf or specify the desired spectrogram x-axis limit.
#' @param yLim the frequency limits (y-axis); default is c(0,10) aka 0-10kHz
#' @param title string for title of plots; default=NULL
#' @param plotLegend logical; include a legend showing amplitude colors? default=FALSE
#' @param onlyPlotSpec logical; do you want to just plot the spec and leave out the legend, axes, and axis labels? default= TRUE
#' @param resampleRate a number in Hz to downsample audio for spectrogram only. This will simplify audio data and speed up generation of spectrogram. Passed to [tuneR::downsample()]. Default=15000 shaves off a few seconds without losing much quality. Put NULL to keep original sample rate for spectrogram. Audiofile will not be resampled for MP4.
#' @param ampTrans amplitude transform for boosting spectrum contrast; default=1 (actual dB values); specify a decimal number for the lambda value of scales::modulus_trans(); 2.5 is a good place to start. (This amplifies your loud values the most, while not increasing background noise much at all)
#' @param min_dB the minimum decibel (quietest sound) to include in the spec; defaults to -30 (-40 would include quieter sounds; -20 would cut out all but very loud sounds)
#' @param filter apply a bandpass filter? Defaults to none (NULL). Expects 'c(0,2)' where sound from 0 to 2kHz would be filtered out
#' @param bg background color (defaults to 1st value of chosen palette)
#' @param wl window length for the spectrogram (low values= higher temporal res; high values= higher freq. res). Default 512 is a good tradeoff
#' @param wl window length for the spectrogram (low values= higher temporal res; high values= higher freq. res). Default 512 is a good tradeoff; human speech would look better at 1024 or higher, giving higher frequency resolution.
#' @param ovlp how much overlap (as percent) between sliding windows to generate spec? Default 90 looks good, but takes longer
#' @param wn window name (slight tweaks on algorithm that affect smoothness of output) see \code{\link[seewave]{spectro}}
#' @param specWidth what width (in inches) would you like to make your PNG output be, if saving a static spec?
Expand Down Expand Up @@ -72,12 +69,15 @@
#' maleBarnSwallow<-prep_static_ggspectro(f[2],destFolder="wd",onlyPlotSpec = FALSE,
#' bgFlood=TRUE,ampTrans=2,min_dB=-40)
#'
#' #much stronger, now let's combine them (you need the cowplot package)
#'
#' # cowplot::plot_grid(femaleBarnSwallow$spec[[1]]+xlim(0,5)+ggtitle("female barn swallow song"),
#' # maleBarnSwallow$spec[[1]]+xlim(0,5)+ggtitle("male barn swallow song"),ncol=1,labels="auto")
#' #much stronger, now let's combine them
#' (you need the patchwork package to use the / operator to stack plots)
#' library(patchwork)
#' (femaleBarnSwallow$spec[[1]]+ggplot2::xlim(0,5)) /
#' (maleBarnSwallow$spec[[1]]+ggplot2::xlim(0,5)) +
#' patchwork::plot_annotation(title="Female and Male barn swallow songs",
#' caption="Female song (top) is much shorter, but similar complexity to males. See: MR Wilkins et al. (2020) Animal Behaviour 168")
#'
#' # ggsave("M&F_barn_swallow_song_specs.jpeg")
#' # ggplot2::ggsave("M&F_barn_swallow_song_specs.jpeg",width=11,height=7)
#'
#' # see more examples at https://marce10.github.io/dynaSpec/
#' }
Expand All @@ -98,9 +98,11 @@ prep_static_ggspectro <-
filter = NULL,
xLim = NULL,
yLim = c(0, 10),
title=NULL,
plotLegend = FALSE,
onlyPlotSpec = TRUE,
ampTrans = 1,
resampleRate = 15000,
min_dB = -30,
wl = 512,
ovlp = 90,
Expand All @@ -122,13 +124,21 @@ prep_static_ggspectro <-
destFolder = dirname(tools::file_path_as_absolute(soundFile))
}
}
destFolder0 <- destFolder
#handle relative destFolder path that ends with /, but doesn't start with /
if(!fs::is_absolute_path(destFolder0)){
destFolder <- fs::path(fs::path_dir(soundFile),destFolder0)
fs::dir_create(destFolder)
message("Relative destFolder supplied:'",destFolder0,"'. Will save files to soundFile parent dir:\n > ",destFolder)
}

#Put soundFile in working dir if requested
if (destFolder == "wd") {
if (destFolder0 == "wd") {
destFolder <- getwd()
}

if (!grepl("/$", destFolder)) {
destFolder = paste0(destFolder, "/")
if (!grepl("/$", destFolder0)) {
destFolder = paste0(destFolder0, "/")
}#if destFolder missing terminal /, add it

if (is.url(soundFile)) {
Expand Down Expand Up @@ -160,7 +170,7 @@ prep_static_ggspectro <-

#Handle file naming for spec
if (is.null(outFilename)) {
outFilename = paste0(tools::file_path_sans_ext(basename(soundFile)), ".PNG")
outFilename = paste0(tools::file_path_sans_ext(basename(soundFile)), ".png")
}
if (!grepl(".png|PNG", outFilename)) {
outFilename = paste0(outFilename, ".png")
Expand All @@ -184,7 +194,7 @@ prep_static_ggspectro <-
}

#Convert MP3s to WAV
if (tools::file_ext(soundFile) == "mp3") {
if (tools::file_ext(soundFile) %in% c("mp3","MP3")) {
print("***Converting mp3 to wav***")
wav0 <- tuneR::readMP3(soundFile)
} else{
Expand All @@ -203,7 +213,7 @@ prep_static_ggspectro <-
crop = crop,
xLim = xLim,
filter = filter,
ampThresh)
ampThresh=ampThresh)

if (length(yLim) == 1) {
yLim = c(0, yLim)
Expand All @@ -214,11 +224,13 @@ prep_static_ggspectro <-
ggSpec(
wav = prepped$newWav,
soundFile = soundFile,
resampleRate = resampleRate,
segLens = prepped$segLens,
savePNG = savePNG,
specWidth = specWidth,
specHeight = specHeight,
destFolder = destFolder,
title = title,
colPal = colPal,
isViridis = isViridis,
crop = crop,
Expand Down Expand Up @@ -252,11 +264,13 @@ prep_static_ggspectro <-
soundFile = soundFile,
destFolder = destFolder,
outFilename = outFilename,
n_pages= length(prepped$segWavs),
crop = crop,
colPal = colPal,
isViridis = isViridis,
xLim = prepped$xLim,
yLim = yLim,
title = title,
plotLegend = plotLegend,
onlyPlotSpec = onlyPlotSpec,
ampTrans = ampTrans,
Expand Down
Loading
Loading