Skip to content

Commit

Permalink
Merge pull request #19 from maRce10/matt-2024-fixes
Browse files Browse the repository at this point in the history
Matt 2024 fixes
  • Loading branch information
maRce10 authored Feb 21, 2024
2 parents 9c4ffa6 + bff482b commit ecc47fc
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 146 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Package: dynaSpec
Type: Package
Title: Dynamic Spectrogram Visualizations
Version: 1.0.1
Date: 2021-03-09
Version: 1.0.2
Date: 2024-02-20
Description: A set of tools to generate dynamic spectrogram visualizations in video format.
License: GPL (>= 2)
Imports: utils, grDevices, graphics, seewave, tuneR, grid, png, ggplot2, viridis, scales, ari, gganimate, warbleR
Expand Down
301 changes: 202 additions & 99 deletions R/ggSpec.R
Original file line number Diff line number Diff line change
@@ -1,109 +1,212 @@
# ggSpec: helper function to create ggplot spectrogram of wave file

ggSpec<-function(wav,soundFile,segLens,savePNG,specWidth,specHeight,destFolder,ovlp,wl,wn,yLim,xLim,colPal,colbins,ampTrans,plotLegend,onlyPlotSpec,isViridis,bg,fontAndAxisCol,min_dB,bgFlood,optim,timeAdj,...)
{
if(missing(segLens)){nSegs=1}else{
nSegs=length(segLens)-1
}


# 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)){
autoFontCol=TRUE
bgRGB<-grDevices::col2rgb(bg)
fontAndAxisCol<-if (bgRGB["red",1]*0.299 + bgRGB["green",1]*0.587 + bgRGB["blue",1]*0.114 > 149){"#000000"}else{"#ffffff"}
}else{autoFontCol=FALSE}
#For testing font contrast against bg
# par(bg=bg)
# plot(1,1,col="transparent")
# text(1,1,"READABLE?",cex=5,col=contrastFont)


#Modified from seewave::ggspectro
#--->
# norm =FALSE is important for having similar gain across different recordings
# spectrogram<-seewave::spectro(wav,plot=FALSE,ovlp=ovlp,wl=wl,wn=wn,norm=FALSE)
#normalize(wav,center=FALSE,pcm=TRUE)
spectrogram<-seewave::spectro(wav,plot=FALSE,ovlp=ovlp,wl=wl,wn=wn,...)
freq <- rep(spectrogram$freq, times = ncol(spectrogram$amp))
time <- rep(spectrogram$time, each = nrow(spectrogram$amp))
amplitude <- as.vector(spectrogram$amp)
#<--------
df <- data.frame(time,freq,amplitude)


#experimental code to simplify the tibble for generating spec data
if(!is.null(optim)){
#nothing here yet
}

#####
#Plot spec for every segment, saving specs as list for output
Glist<-list()
for (i in 1:(nSegs)){
ggSpec <-
function(wav,
soundFile,
segLens,
savePNG,
specWidth,
specHeight,
destFolder,
ovlp,
wl,
wn,
yLim,
xLim,
colPal,
colbins,
ampTrans,
plotLegend,
onlyPlotSpec,
isViridis,
bg,
fontAndAxisCol,
min_dB,
bgFlood,
optim,
timeAdj,
...)
{
if (missing(segLens)) {
nSegs = 1
} else{
nSegs = length(segLens) - 1
}


# 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)) {
autoFontCol = TRUE
bgRGB <- grDevices::col2rgb(bg)
fontAndAxisCol <-
if (bgRGB["red", 1] * 0.299 + bgRGB["green", 1] * 0.587 + bgRGB["blue", 1] *
0.114 > 149) {
"#000000"
} else{
"#ffffff"
}
} else{
autoFontCol = FALSE
}
#For testing font contrast against bg
# par(bg=bg)
# plot(1,1,col="transparent")
# text(1,1,"READABLE?",cex=5,col=contrastFont)

#subset df for segments
if(nSegs>1){
if(i==1){
cat("\nSpectrogram ggplots of segmented WAVs being generated\n")
}
if(i==nSegs){
df_i <- subset(df,time>=segLens[i]&time<=segLens[i+1]) #last segment is inclusive on left & right bounds
}else{
df_i<-subset(df,time>=segLens[i]&time<segLens[i+1]) #all other segs inclusive on left

#Modified from seewave::ggspectro
#--->
# norm =FALSE is important for having similar gain across different recordings
# spectrogram<-seewave::spectro(wav,plot=FALSE,ovlp=ovlp,wl=wl,wn=wn,norm=FALSE)
#normalize(wav,center=FALSE,pcm=TRUE)
spectrogram <-
seewave::spectro(
wav,
plot = FALSE,
ovlp = ovlp,
wl = wl,
wn = wn,
...
)
freq <- rep(spectrogram$freq, times = ncol(spectrogram$amp))
time <- rep(spectrogram$time, each = nrow(spectrogram$amp))
amplitude <- as.vector(spectrogram$amp)
#<--------
df <- data.frame(time, freq, amplitude)


#experimental code to simplify the tibble for generating spec data
if (!is.null(optim)) {
#nothing here yet
}

#####
#Plot spec for every segment, saving specs as list for output
Glist <- list()
for (i in 1:(nSegs)) {
#subset df for segments
if (nSegs > 1) {
if (i == 1) {
cat("\nSpectrogram ggplots of segmented WAVs being generated\n")
}
if (i == nSegs) {
df_i <-
subset(df, time >= segLens[i] &
time <= segLens[i + 1]) #last segment is inclusive on left & right bounds
} else{
df_i <-
subset(df, time >= segLens[i] &
time < segLens[i + 1]) #all other segs inclusive on left
}

} else{
df_i = df
}

}else{df_i=df}

..level.. <- NULL

#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")+{
#Set scale according to viridis or custom color scheme
if(isViridis){viridis::scale_fill_viridis(limits=c(min_dB,0),na.value="transparent",option=colPal,trans=scales::modulus_trans(p=ampTrans))}else{
ggplot2::scale_fill_gradient(limits=c(min_dB,0),na.value="transparent",low=colPal[1],high=colPal[2],trans=scales::modulus_trans(p=ampTrans))}
}+
#Make contours
ggplot2::stat_contour(geom="polygon",ggplot2::aes(fill=..level..),bins=colbins,na.rm=TRUE)+
#Set base theme
mytheme(bg)+{
#If user supplied fontAndAxisCol, change those settings (regardless of whether bg is flooded or not)
if(!autoFontCol){
ggplot2::theme(axis.text=ggplot2::element_text(colour=fontAndAxisCol),text=ggplot2::element_text(colour=fontAndAxisCol),axis.line = ggplot2::element_line(colour=fontAndAxisCol),axis.ticks=ggplot2::element_line(colour=fontAndAxisCol))
}else{}
}+{
#get rid of axes & legend if requested
if(onlyPlotSpec){ggplot2::theme_void()+ ggplot2::theme(plot.background=ggplot2::element_rect(fill=bg),text=ggplot2::element_text(colour=fontAndAxisCol))
}else{
#For cases where axes are plotted

level=NULL #just to shut up the check

#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") +
{
#Set scale according to viridis or custom color scheme
if (isViridis) {
viridis::scale_fill_viridis(
limits = c(min_dB, 0),
na.value = "transparent",
option = colPal,
trans = scales::modulus_trans(p = ampTrans)
)
} else{
ggplot2::scale_fill_gradient(
limits = c(min_dB, 0),
na.value = "transparent",
low = colPal[1],
high = colPal[2],
trans = scales::modulus_trans(p = ampTrans)
)
}
} +
#Make contours
ggplot2::stat_contour(
geom = "polygon",
ggplot2::aes(fill = ggplot2::after_stat(level)),
bins = colbins,
na.rm = TRUE
) +
#Set base theme
mytheme(bg) + {
#If user supplied fontAndAxisCol, change those settings (regardless of whether bg is flooded or not)
if (!autoFontCol) {
ggplot2::theme(
axis.text = ggplot2::element_text(colour = fontAndAxisCol),
text = ggplot2::element_text(colour = fontAndAxisCol),
axis.line = ggplot2::element_line(colour = fontAndAxisCol),
axis.ticks = ggplot2::element_line(colour = fontAndAxisCol)
)
} else{
}
} + {
#get rid of axes & legend if requested
if (onlyPlotSpec) {
ggplot2::theme_void() + ggplot2::theme(
plot.background = ggplot2::element_rect(fill = bg),
text = ggplot2::element_text(colour = fontAndAxisCol)
)
} else{
#For cases where axes are plotted
# if axes to be plotted, flood panel bg color over axis area?
if(bgFlood){ggplot2::theme(plot.background=ggplot2::element_rect(fill=bg),axis.text=ggplot2::element_text(colour=fontAndAxisCol),text=ggplot2::element_text(colour=fontAndAxisCol),axis.line = ggplot2::element_line(colour=fontAndAxisCol),axis.ticks=ggplot2::element_line(colour=fontAndAxisCol),legend.background=ggplot2::element_rect(fill=bg))}else{}
if (bgFlood) {
ggplot2::theme(
plot.background = ggplot2::element_rect(fill = bg),
axis.text = ggplot2::element_text(colour = fontAndAxisCol),
text = ggplot2::element_text(colour = fontAndAxisCol),
axis.line = ggplot2::element_line(colour = fontAndAxisCol),
axis.ticks = ggplot2::element_line(colour = fontAndAxisCol),
legend.background = ggplot2::element_rect(fill = bg)
)
} else{
}
}+{
#Get rid of plotLegend if requested
if(!plotLegend){ggplot2::theme(legend.position = "none")}else{ggplot2::theme(legend.position = "right")}
}
} + {
#Get rid of plotLegend if requested
if (!plotLegend) {
ggplot2::theme(legend.position = "none")
} else{
ggplot2::theme(legend.position = "right")
}
}#end GGPLOT stuffs

#Save plots if requested
if(savePNG){
if(i==1){
baseNom<-basename(tools::file_path_sans_ext(soundFile))
subDest<-fs::path(destFolder,paste0(baseNom,"_static_specs"))
dir.create(subDest,showWarnings = FALSE)

#Save plots if requested
if (savePNG) {
if (i == 1) {
baseNom <- basename(tools::file_path_sans_ext(soundFile))
subDest <-
fs::path(destFolder, paste0(baseNom, "_static_specs"))
dir.create(subDest, showWarnings = FALSE)
}
fn_i=fs::path(subDest,paste0(baseNom,"_",i),ext="png")
ggplot2::ggsave(fn_i,width=specWidth,height=specHeight,units="in")
cat(paste0("\nStatic spec saved @",fn_i))
}
fn_i = fs::path(subDest, paste0(baseNom, "_", i), ext = "png")
ggplot2::ggsave(fn_i,
width = specWidth,
height = specHeight,
units = "in")
cat(paste0("\nStatic spec saved @", fn_i))
}

}#end for loop

}#end for loop

rm(spectrogram)

return(list(specList=Glist,fontAndAxisCol=fontAndAxisCol,autoFontCol=autoFontCol))
}#end

rm(spectrogram)

return(list(
specList = Glist,
fontAndAxisCol = fontAndAxisCol,
autoFontCol = autoFontCol
))
}#end
1 change: 1 addition & 0 deletions R/paged_spectro.R
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ for(i in 1:length(specParams$segWavs))
spec_height_px<-attributes(spec_PNG)$dim[1]

#Create data frame for highlighting box animation for i^th wav segment

range_i<-c((i-1)*specParams$xLim[2],(i-1)*specParams$xLim[2]+specParams$xLim[2])
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)))
Expand Down
2 changes: 1 addition & 1 deletion R/prep_static_ggspectro.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#' @param savePNG 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)
#' @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?
Expand Down
Loading

0 comments on commit ecc47fc

Please sign in to comment.